23種設(shè)計模式-裝飾模式

  1. 罪惡的成績單

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


17-1

我們先來看成績單的抽象類SchoolReport,代碼如下:

public abstract class SchoolReport {
    //顯示成績情況
    public abstract void report();
    //要家長簽字
    public abstract void sign();
}

有抽象類了,在來看看具體的四年級成績單FouthGradeSchoolReport,代碼如下:

public class FouthGradeSchoolReport extends SchoolReport {
    @Override
    public void report() {
        //成績單的格式
        System.out.println("尊敬的xxx家長:");
        System.out.println("..............");
        System.out.println("語文 62 數(shù)學(xué) 65 體育 98 自然 63");
        System.out.println("..............:");
        System.out.println("家長簽名:");
    }

    @Override
    public void sign(String name) {
        System.out.println("家長簽名:" + name);
    }
}

成績單出來62、65之類的在小學(xué)基本就是墊底,那么我們把成績單帶回去給家長看,修改一下類圖,如圖17-2:


17-2

這是原裝的成績單,沒有動過的,來看Father類代碼如下:

public class Father {
    public static void main(String[] args) {
        SchoolReport sc = new FouthGradeSchoolReport();
        //看成績單
        sc.report();
        //簽名?休想,一頓胖揍
    }
}

這個成績直接拿出來肯定是找打,我們把成績單封裝一下,封裝分為兩步來實(shí)現(xiàn):

  • 匯報最高成績
    跟家長說各個科目的最高分,語文最高75,數(shù)學(xué)最高78,自然是80;那么家長一看你的成績和最高分差不多,也還行;實(shí)際普遍在70以上,這個60多還是墊底
  • 匯報排名
    全班排第38名,但是不能說參加考試的只有40個人,反正成績單上沒寫多少人,相比較之前的40多名還是有進(jìn)步的,哈哈
    這就是加上了一些修飾,我們看看類圖如何修改,如圖17-3:


    17-3

    這應(yīng)該是常規(guī)的處理方式了,直接增加一個子類,覆寫report方法,我們看具體的實(shí)現(xiàn),代碼如下:

public class SugarFouthGradeSchoolReport extends FouthGradeSchoolReport {
    private void reportHighScore(){
        System.out.println("這次考試語文最高是75,數(shù)學(xué)是78,自然是80");
    }
    private void reportSort(){
        System.out.println("我是排名第38名。。。");
    }
    @Override
    public void report(){
        this.reportHighScore();
        super.report();
        this.reportSort();
    }
}

通過繼承確實(shí)能夠解決這個問題,但是這個只能作為應(yīng)急的一種方式,如果需要繼續(xù)加裝飾那怎么辦,而且裝飾條件不是一次就明確的,可能過一段時間加一個,那一直通過繼承來處理肯定是不合理的。在面向?qū)ο蟮脑O(shè)計中,如果超過兩層,是不是該考慮設(shè)計的問題了,這是經(jīng)驗(yàn),不是絕對,繼承的層次越多以后的維護(hù)成本越多,那這么解決這個裝飾的問題呢,我們專門定義一批負(fù)責(zé)裝飾的類,然后根據(jù)實(shí)際情況來決定是否需要進(jìn)行裝飾,類圖如17-4:


17-4

增加一個抽象類和兩個實(shí)現(xiàn)類,其中Decorator的作用是封裝SchoolReport類,如果大家還記得代理模式,那么很容易看懂這個類圖,裝飾類的作用也是一個特殊的代理類,真實(shí)的執(zhí)行者還是被代理的角色FouthGradeSchoolReport,代碼如下:

public abstract class Decorator extends SchoolReport {
    //首先得知道是哪個成績單
    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);
    }
}

裝飾類還是把動作的執(zhí)行委托給需要裝飾的對象,Decorator抽象類的目的很簡單,就是要讓子類來封裝SchoolReport的子類,先看HighScoreDecorator實(shí)現(xiàn)類,代碼如下:

public class HighScoreDecorator extends Decorator {

    public HighScoreDecorator(SchoolReport schoolReport) {
        super(schoolReport);
    }
    private void reportHighScore(){
        System.out.println("這次考試語文最高是75,數(shù)學(xué)是78,自然是80");
    }

    @Override
    public void report() {
        this.reportHighScore();
        super.report();
    }
}

重寫了report方法,先調(diào)用具體裝飾類的裝飾方法reportHighScore,然后再調(diào)用具體構(gòu)件的方法,我們再來看怎么匯報學(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();
    }
}

通過這兩個強(qiáng)力的修飾工具,然后就可以拿一份"漂亮"的成績單出來了,代碼如下:

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("老三");
    }
}

這就是裝飾者模式,可以處理一些用繼承來處理的問題,但是比繼承更加的靈活,但是裝飾過多層之后就不太友好了,就像繼承過多之后可讀性就變的很差了。

  1. 裝飾者模式的定義

裝飾模式(Decorator Pattern)是一種比較常見的模式,其定義如下:Attach additional responsibilities to an object dynamically keeping the same interface.Decorators provide a flexible alternative to subclassing for extending functionality.(動態(tài)地給一個對象添加一些額外的職責(zé)。就增加功能來說,裝飾模式相比生成子類更為靈活。)
類圖17-5:


17-5

在類圖中,有四個角色需要說明:

  • Component抽象構(gòu)件
    Component是一個接口或者是抽象類,就是定義我們最核心的對象,也就是最原始的對象,如上面的成績單。
    注意 在裝飾模式中,必然有一個最基本、最核心、最原始的接口或抽象類充當(dāng) Component抽象構(gòu)件(這個抽象構(gòu)件應(yīng)該是描述我們想要裝飾的部分
  • ConcreteComponent具體構(gòu)件
    ConcreteComponent是最核心、最原始、最基本的接口或抽象類的實(shí)現(xiàn),就是我們要裝飾的類,就是對應(yīng)的例子中的FouthGradeSchoolReport
    -Decorator裝飾角色
    一般是一個抽象類,實(shí)現(xiàn)接口或抽象方法,在他的屬性里必然有一個private變量指向Component抽象構(gòu)件。(裝飾者模式的核心,和被裝飾者實(shí)現(xiàn)或繼承相同抽象,然后私有屬性指向被裝飾者
    -具體裝飾角色
    ConcreteDecoratorA和ConcreteDecoratorB是兩個具體的裝飾類,這個就是用來裝飾的了,想從哪個方面哪個角度去裝飾,就實(shí)現(xiàn)Decorator去寫自己的裝飾方法就好了。
    我們來看一下裝飾者模式的通用代碼:
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();
    }
}

(這個裝飾者模式和代理模式非常相近,而且都是用來增強(qiáng)被裝飾類(被代理類)的;但是這兩者的具體的區(qū)別和用法又是怎么樣的呢)

  1. 裝飾者模式應(yīng)用

3.1裝飾者模式的優(yōu)點(diǎn)

  • 裝飾類和被裝飾類可以獨(dú)立發(fā)展,而不會互相耦合。換句話說,Component類無須知道Decorator類,Decorator類是從外部來擴(kuò)展Component類的功能,而Decorator也不用知道具體的構(gòu)建。
  • 裝飾者模式是繼承模式的一個替代方案。我們看裝飾類Decorator,不管裝飾多少層,返回的對象還是Component,實(shí)現(xiàn)的還是is-a的關(guān)系。
  • 裝飾模式可以動態(tài)地擴(kuò)展一個實(shí)現(xiàn)類的功能,而且還不會影響到被擴(kuò)展的類,非常符合開閉原則
    3.2裝飾模式的缺點(diǎn)
    裝飾者模式雖然可以很好的替代繼承的方式處理問題,并且比繼承更加靈活 ,但是也同樣帶來了和繼承類似的問題,那就是裝飾層數(shù)過多的問題,這個就只能靈活處理;
    3.3 裝飾模式的使用場景
  • 需要擴(kuò)展一個類的功能,或給一個類增加附加功能;
  • 需要動態(tài)地給一個對象增加功能,這些功能還可以動態(tài)的撤銷;
  • 需要為一批的兄弟類進(jìn)行改裝或加裝功能,當(dāng)然是首選裝飾模式
  1. 最佳實(shí)踐

裝飾模式是對繼承的有力補(bǔ)充,而且裝飾模式比繼承更加的靈活,易維護(hù),易擴(kuò)展,衣服用;這個之前學(xué)習(xí)例子的時候就有很好的體會。
這里有一點(diǎn)就是裝飾模式和代理模式的對比,這一章中并沒有提出,有興趣可以百度一下;我們學(xué)完23種設(shè)計模式后,會接著學(xué)后續(xù)的相似的設(shè)計模式之前的對比。

內(nèi)容來自《設(shè)計模式之禪》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容