-
罪惡的成績(jī)單
考試成績(jī)單以及成績(jī)排名,大家都懂得,以前上學(xué)的時(shí)候,這玩意往家里寄是真的要命。成績(jī)單還需要家長(zhǎng)簽字,那么,我們來(lái)模擬將成績(jī)單給家長(zhǎng)簽字這一情景,如圖17-1:

我們先來(lái)看成績(jī)單的抽象類SchoolReport,代碼如下:
public abstract class SchoolReport {
//顯示成績(jī)情況
public abstract void report();
//要家長(zhǎng)簽字
public abstract void sign();
}
有抽象類了,在來(lái)看看具體的四年級(jí)成績(jī)單FouthGradeSchoolReport,代碼如下:
public class FouthGradeSchoolReport extends SchoolReport {
@Override
public void report() {
//成績(jī)單的格式
System.out.println("尊敬的xxx家長(zhǎng):");
System.out.println("..............");
System.out.println("語(yǔ)文 62 數(shù)學(xué) 65 體育 98 自然 63");
System.out.println("..............:");
System.out.println("家長(zhǎng)簽名:");
}
@Override
public void sign(String name) {
System.out.println("家長(zhǎng)簽名:" + name);
}
}
成績(jī)單出來(lái)62、65之類的在小學(xué)基本就是墊底,那么我們把成績(jī)單帶回去給家長(zhǎng)看,修改一下類圖,如圖17-2:

這是原裝的成績(jī)單,沒有動(dòng)過(guò)的,來(lái)看Father類代碼如下:
public class Father {
public static void main(String[] args) {
SchoolReport sc = new FouthGradeSchoolReport();
//看成績(jī)單
sc.report();
//簽名?休想,一頓胖揍
}
}
這個(gè)成績(jī)直接拿出來(lái)肯定是找打,我們把成績(jī)單封裝一下,封裝分為兩步來(lái)實(shí)現(xiàn):
- 匯報(bào)最高成績(jī)
跟家長(zhǎng)說(shuō)各個(gè)科目的最高分,語(yǔ)文最高75,數(shù)學(xué)最高78,自然是80;那么家長(zhǎng)一看你的成績(jī)和最高分差不多,也還行;實(shí)際普遍在70以上,這個(gè)60多還是墊底 -
匯報(bào)排名
全班排第38名,但是不能說(shuō)參加考試的只有40個(gè)人,反正成績(jī)單上沒寫多少人,相比較之前的40多名還是有進(jìn)步的,哈哈
這就是加上了一些修飾,我們看看類圖如何修改,如圖17-3:
17-3
這應(yīng)該是常規(guī)的處理方式了,直接增加一個(gè)子類,覆寫report方法,我們看具體的實(shí)現(xiàn),代碼如下:
public class SugarFouthGradeSchoolReport extends FouthGradeSchoolReport {
private void reportHighScore(){
System.out.println("這次考試語(yǔ)文最高是75,數(shù)學(xué)是78,自然是80");
}
private void reportSort(){
System.out.println("我是排名第38名。。。");
}
@Override
public void report(){
this.reportHighScore();
super.report();
this.reportSort();
}
}
通過(guò)繼承確實(shí)能夠解決這個(gè)問(wèn)題,但是這個(gè)只能作為應(yīng)急的一種方式,如果需要繼續(xù)加裝飾那怎么辦,而且裝飾條件不是一次就明確的,可能過(guò)一段時(shí)間加一個(gè),那一直通過(guò)繼承來(lái)處理肯定是不合理的。在面向?qū)ο蟮脑O(shè)計(jì)中,如果超過(guò)兩層,是不是該考慮設(shè)計(jì)的問(wèn)題了,這是經(jīng)驗(yàn),不是絕對(duì),繼承的層次越多以后的維護(hù)成本越多,那這么解決這個(gè)裝飾的問(wèn)題呢,我們專門定義一批負(fù)責(zé)裝飾的類,然后根據(jù)實(shí)際情況來(lái)決定是否需要進(jìn)行裝飾,類圖如17-4:

增加一個(gè)抽象類和兩個(gè)實(shí)現(xiàn)類,其中Decorator的作用是封裝SchoolReport類,如果大家還記得代理模式,那么很容易看懂這個(gè)類圖,裝飾類的作用也是一個(gè)特殊的代理類,真實(shí)的執(zhí)行者還是被代理的角色FouthGradeSchoolReport,代碼如下:
public abstract class Decorator extends SchoolReport {
//首先得知道是哪個(gè)成績(jī)單
private SchoolReport schoolReport;
public Decorator(SchoolReport schoolReport){
this.schoolReport = schoolReport;
}
@Override
public void report(){
this.schoolReport.report();
}
@Override
public void sign(String name){
this.schoolReport.sign(name);
}
}
裝飾類還是把動(dòng)作的執(zhí)行委托給需要裝飾的對(duì)象,Decorator抽象類的目的很簡(jiǎn)單,就是要讓子類來(lái)封裝SchoolReport的子類,先看HighScoreDecorator實(shí)現(xiàn)類,代碼如下:
public class HighScoreDecorator extends Decorator {
public HighScoreDecorator(SchoolReport schoolReport) {
super(schoolReport);
}
private void reportHighScore(){
System.out.println("這次考試語(yǔ)文最高是75,數(shù)學(xué)是78,自然是80");
}
@Override
public void report() {
this.reportHighScore();
super.report();
}
}
重寫了report方法,先調(diào)用具體裝飾類的裝飾方法reportHighScore,然后再調(diào)用具體構(gòu)件的方法,我們?cè)賮?lái)看怎么匯報(bào)學(xué)校排序情況SortDecorator代碼,代碼如下:
public class SortDecorator extends Decorator {
public SortDecorator(SchoolReport schoolReport) {
super(schoolReport);
}
private void reportSort(){
System.out.println("我是排名第38名。。。");
}
@Override
public void report(){
super.report();
this.reportSort();
}
}
通過(guò)這兩個(gè)強(qiáng)力的修飾工具,然后就可以拿一份"漂亮"的成績(jī)單出來(lái)了,代碼如下:
public class Client {
public static void main(String[] args) {
SchoolReport schoolReport;
schoolReport = new FouthGradeSchoolReport();
schoolReport = new HighScoreDecorator(schoolReport);
schoolReport = new SortDecorator(schoolReport);
schoolReport.report();
schoolReport.sign("老三");
}
}
這就是裝飾者模式,可以處理一些用繼承來(lái)處理的問(wèn)題,但是比繼承更加的靈活,但是裝飾過(guò)多層之后就不太友好了,就像繼承過(guò)多之后可讀性就變的很差了。
-
裝飾者模式的定義
裝飾模式(Decorator Pattern)是一種比較常見的模式,其定義如下:Attach additional responsibilities to an object dynamically keeping the same interface.Decorators provide a flexible alternative to subclassing for extending functionality.(動(dòng)態(tài)地給一個(gè)對(duì)象添加一些額外的職責(zé)。就增加功能來(lái)說(shuō),裝飾模式相比生成子類更為靈活。)
類圖17-5:

在類圖中,有四個(gè)角色需要說(shuō)明:
- Component抽象構(gòu)件
Component是一個(gè)接口或者是抽象類,就是定義我們最核心的對(duì)象,也就是最原始的對(duì)象,如上面的成績(jī)單。
注意 在裝飾模式中,必然有一個(gè)最基本、最核心、最原始的接口或抽象類充當(dāng) Component抽象構(gòu)件(這個(gè)抽象構(gòu)件應(yīng)該是描述我們想要裝飾的部分) - ConcreteComponent具體構(gòu)件
ConcreteComponent是最核心、最原始、最基本的接口或抽象類的實(shí)現(xiàn),就是我們要裝飾的類,就是對(duì)應(yīng)的例子中的FouthGradeSchoolReport
-Decorator裝飾角色
一般是一個(gè)抽象類,實(shí)現(xiàn)接口或抽象方法,在他的屬性里必然有一個(gè)private變量指向Component抽象構(gòu)件。(裝飾者模式的核心,和被裝飾者實(shí)現(xiàn)或繼承相同抽象,然后私有屬性指向被裝飾者)
-具體裝飾角色
ConcreteDecoratorA和ConcreteDecoratorB是兩個(gè)具體的裝飾類,這個(gè)就是用來(lái)裝飾的了,想從哪個(gè)方面哪個(gè)角度去裝飾,就實(shí)現(xiàn)Decorator去寫自己的裝飾方法就好了。
我們來(lái)看一下裝飾者模式的通用代碼:
public abstract class Component {
public abstract void operation();
}
public class ConcreateComponent extends Component {
@Override
public void operation() {
//需要被裝飾的方法
System.out.println("do something");
}
}
public class Decorator extends Component {
private Component component;
public Decorator(Component component){
this.component = component;
}
@Override
public void operation() {
//委托給裝飾者執(zhí)行
component.operation();
}
}
public class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component component) {
super(component);
}
private void method(){
//do something
}
@Override
public void operation() {
this.method();
super.operation();
}
}
(這個(gè)裝飾者模式和代理模式非常相近,而且都是用來(lái)增強(qiáng)被裝飾類(被代理類)的;但是這兩者的具體的區(qū)別和用法又是怎么樣的呢)
-
裝飾者模式應(yīng)用
3.1裝飾者模式的優(yōu)點(diǎn)
- 裝飾類和被裝飾類可以獨(dú)立發(fā)展,而不會(huì)互相耦合。換句話說(shuō),Component類無(wú)須知道Decorator類,Decorator類是從外部來(lái)擴(kuò)展Component類的功能,而Decorator也不用知道具體的構(gòu)建。
- 裝飾者模式是繼承模式的一個(gè)替代方案。我們看裝飾類Decorator,不管裝飾多少層,返回的對(duì)象還是Component,實(shí)現(xiàn)的還是is-a的關(guān)系。
- 裝飾模式可以動(dòng)態(tài)地?cái)U(kuò)展一個(gè)實(shí)現(xiàn)類的功能,而且還不會(huì)影響到被擴(kuò)展的類,非常符合開閉原則
3.2裝飾模式的缺點(diǎn)
裝飾者模式雖然可以很好的替代繼承的方式處理問(wèn)題,并且比繼承更加靈活 ,但是也同樣帶來(lái)了和繼承類似的問(wèn)題,那就是裝飾層數(shù)過(guò)多的問(wèn)題,這個(gè)就只能靈活處理;
3.3 裝飾模式的使用場(chǎng)景 - 需要擴(kuò)展一個(gè)類的功能,或給一個(gè)類增加附加功能;
- 需要?jiǎng)討B(tài)地給一個(gè)對(duì)象增加功能,這些功能還可以動(dòng)態(tài)的撤銷;
- 需要為一批的兄弟類進(jìn)行改裝或加裝功能,當(dāng)然是首選裝飾模式
-
最佳實(shí)踐
裝飾模式是對(duì)繼承的有力補(bǔ)充,而且裝飾模式比繼承更加的靈活,易維護(hù),易擴(kuò)展,衣服用;這個(gè)之前學(xué)習(xí)例子的時(shí)候就有很好的體會(huì)。
(這里有一點(diǎn)就是裝飾模式和代理模式的對(duì)比,這一章中并沒有提出,有興趣可以百度一下;我們學(xué)完23種設(shè)計(jì)模式后,會(huì)接著學(xué)后續(xù)的相似的設(shè)計(jì)模式之前的對(duì)比。)
內(nèi)容來(lái)自《設(shè)計(jì)模式之禪》
