大話設(shè)計(jì)模式——裝飾模式

需求

寫一個(gè)給人模擬搭配不同服飾的程序,可以給人換各種各樣的衣服褲子的形象。

初步實(shí)現(xiàn)

需求比較簡(jiǎn)單,直接上代碼:

public class Person {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void wearTShirt(){
        System.out.print("大T恤 ");
    }

    public void wearBigTrouser(){
        System.out.print("垮褲 ");
    }

    public void wearSneakers(){
        System.out.print("破球鞋 ");
    }

    public void wearSuit(){
        System.out.print("西裝 ");
    }

    public void wearTie(){
        System.out.print("領(lǐng)帶 ");
    }

    public void wearLeatherShoes(){
        System.out.print("皮鞋 ");
    }

    public void show(){
        System.out.println("裝扮的"+name);
    }
}

客戶端代碼:

public class Client {
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("小明");

        person.wearTShirt();
        person.wearBigTrouser();
        person.wearSneakers();
        person.show();

        person.wearSuit();
        person.wearTie();
        person.wearLeatherShoes();
        person.show();
    }
}

運(yùn)行結(jié)果:


程序運(yùn)行結(jié)果

分析

通過前面幾節(jié)的介紹,可以發(fā)現(xiàn)這種寫法的顯著毛病:難以實(shí)現(xiàn)功能拓展。比如現(xiàn)在不僅僅只有這兩種搭配方式,額外需要添加其他的搭配方式,該怎么辦呢?這種寫法必然需要對(duì)Person類進(jìn)行手術(shù),違反了開閉原則。

改進(jìn)實(shí)現(xiàn)

很容易結(jié)合前面的實(shí)例,我們利用面向?qū)ο蟮某绦蛟O(shè)計(jì)方式,通過創(chuàng)造服飾抽象類,讓其他具體的西裝、領(lǐng)帶、垮褲等來繼承,實(shí)現(xiàn)程序功能的可擴(kuò)展。
UML類圖:

UML類圖

Person類實(shí)現(xiàn):

public class Person {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void show() {
        System.out.println("裝扮的" + name);
    }
}

服飾基類:

public abstract class Finery {
    public abstract void show();
}

其他具體衣服的實(shí)現(xiàn),以大T恤、西裝為例:

public class TShirt extends Finery {
    @Override
    public void show() {
        System.out.println("大T恤");
    }
}
public class Suit extends Finery {
    @Override
    public void show() {
        System.out.println("西裝");
    }
}

客戶端代碼:

public class Client {
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("小明");

        Finery xz = new Suit();
        Finery ld = new Tie();
        Finery px = new LeatherShoes();

        xz.show();
        ld.show();
        px.show();
        person.show();
    }
}

再次分析

<<大話設(shè)計(jì)模式>>49頁(yè)中提及,上面這種改進(jìn)方案雖然實(shí)現(xiàn)了服飾與人的分離,且利用面向?qū)ο蟪绦蛟O(shè)計(jì)的特性可以實(shí)現(xiàn)功能的擴(kuò)展,但是仍然存在一個(gè)顯著的問題——穿衣服的動(dòng)作是一步一步完全暴露在外面的。

xz.show();
ld.show();
px.show();

通過在客戶端,一步步的進(jìn)行穿著服飾,這種設(shè)計(jì)方案是不夠良好的。(但實(shí)際開發(fā)中不好在哪,我也沒有深刻理解。)這些操作應(yīng)該在內(nèi)部組裝完畢,同時(shí)也要將需要的功能(服飾)按照一定的順序進(jìn)行串聯(lián),不能先打領(lǐng)帶后穿襯衫等...而裝飾模式就是解決這一需求的。

裝飾模式

  • 定義:動(dòng)態(tài)給一個(gè)對(duì)象添加一些額外的職責(zé),使用Decorator模式相比用生成子類方式達(dá)到功能的擴(kuò)充顯得更為靈活。
  • 設(shè)計(jì)初衷:通常可以使用繼承來實(shí)現(xiàn)功能的拓展,如果這些需要拓展的功能的種類很繁多,那么勢(shì)必生成很多子類,增加系統(tǒng)的復(fù)雜性,同時(shí),使用繼承實(shí)現(xiàn)功能拓展,我們必須可預(yù)見這些拓展功能,這些功能是編譯時(shí)就確定了,是靜態(tài)的。

裝飾模式的使用

UML類圖:

UML類圖

這里需要注意的是,裝飾模式,裝飾類與被裝飾類都是繼承自同一父類,究竟是為什么?這一點(diǎn)在客戶端代碼中可以體現(xiàn),需要好好體會(huì),這也是理解裝飾模式的關(guān)鍵所在。

代碼實(shí)現(xiàn):

  1. Component抽象類——被裝飾者的抽象類,實(shí)際中也不一定有此類:

    public abstract class Component {
        public abstract void meathod();
    }
    
  2. 具體的被裝飾者類:

    public class ConcreteComponent extends Component {
        @Override
        public void meathod() {
            System.out.println("具體要被裝飾的對(duì)象的方法");
        }
    }
    
  3. Decorator抽象類——裝飾者抽象類:

    public abstract class Decorator extends Component {
        private Component component;
    
        public void setComponent(Component component) {
            this.component = component;
        }
    
        @Override
        public void meathod() {
            if (component != null)
                component.meathod();
        }
    }
    
  4. 具體的裝飾類A,B:

    public class ConcreteDecoratorA extends Decorator {
    
        private void addNewMethodOfA(){
            //A裝飾器的作用
            System.out.println("A裝飾器的作用");
        }
    
        @Override
        public void meathod() {
            super.meathod();
            addNewMethodOfA();
        }
    }
    
    public class ConcreteDecoratorB extends Decorator {
    
        private void addNewMethodOfB(){
            //B裝飾器的作用
            System.out.println("B裝飾器的作用");
        }
    
        @Override
        public void meathod() {
            super.meathod();
            addNewMethodOfB();
        }
    }
    
  5. 客戶端類:

    public class Client {
    
        public static void main(String[] args) {
            Component component = new ConcreteComponent();
            Decorator decoratorA = new ConcreteDecoratorA();
            Decorator decoratorB = new ConcreteDecoratorB();
    
            decoratorA.setComponent(component);
            decoratorB.setComponent(decoratorA);
            decoratorB.meathod();
        }
    }
    

總結(jié):

  • 裝飾模式是用setComponent()方法來進(jìn)行裝飾,其實(shí)也就是功能擴(kuò)展;
  • 每個(gè)裝飾類裝飾什么樣的功能和這個(gè)裝飾類如何被調(diào)用進(jìn)行裝飾是分離的,即裝飾類A/B能完成什么樣的裝飾與他們什么時(shí)候被調(diào)用、按照什么順序是無關(guān)的。

利用裝飾模式實(shí)現(xiàn)換衣

UML類圖:

UML類圖

代碼實(shí)現(xiàn):

  1. Person類——被裝飾者;

    public class Person {
        private String name;
    
        public void setName(String name) {
            this.name = name;
        }
    
        public void show() {
            System.out.println("裝扮的" + name);
        }
    }
    
  2. Finery類——抽象裝飾類;

    public abstract class Finery extends Person {
    
        private Person person;
    
        public void setPerson(Person person) {
            this.person = person;
        }
    
        @Override
        public void show() {
            if (person != null)
                person.show();
        }
    }
    
  3. 其他具體裝飾類;

    public class BigTrousers extends Finery {
        @Override
        public void show() {
            super.show();
            System.out.println("垮褲");
        }
    }
    
  4. 客戶端類;

    public class Client {
        public static void main(String[] args) {
            Person person = new Person();
            person.setName("小明");
    
            Finery xz = new Suit();
            Finery ld = new Tie();
            Finery px = new LeatherShoes();
    
            xz.setPerson(person);
            ld.setPerson(xz);
            px.setPerson(ld);
            px.show();
        }
    }
    
  5. 運(yùn)行結(jié)果:


    運(yùn)行結(jié)果

裝飾模式小結(jié)

  • 裝飾模式是為已有功能動(dòng)態(tài)的添加更多功能的一種方式;
  • 裝飾模式把類中的裝飾功能從類中搬移去除,這樣可以簡(jiǎn)化原有的類。同時(shí)有效的把類的核心職責(zé)和裝飾功能區(qū)分開,而且可以 去除相關(guān)類中重復(fù)的裝飾邏輯 ;(后半句話怎么理解呢?望評(píng)論出你的看法!)
  • 當(dāng)系統(tǒng)需要新功能時(shí),是向舊的類中添加新的代碼。這些新的代碼通常裝飾了原有類的核心職責(zé)或主要行為。但這種做法的問題在于,他們?cè)谥黝愔屑尤肓诵碌淖侄?、新的方法和新的邏輯,從而增加了主類的?fù)雜度,而這些新加入的東西僅僅是為了滿足一些只在某種特定情況下才會(huì)執(zhí)行的特殊行為的需要。裝飾模式提供了一種非常好的解決方案,它把每個(gè)要裝飾的功能放在單獨(dú)的類中,并讓這個(gè)類包裝它所要裝飾的對(duì)象,因此,當(dāng)需要執(zhí)行特殊行為時(shí),客戶代碼就可以根據(jù)需要、有選擇按順序的使用裝飾功能包裝對(duì)象;
  • 對(duì)于前面所說為什么裝飾模式,被裝飾者與裝飾者都要繼承同一父類,這一點(diǎn)我覺得相當(dāng)于給人穿了件衣服后返回的新對(duì)象畢竟本質(zhì)還是人,只是多了件衣服,這樣就可以不斷的穿衣服不斷的返回。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 1 場(chǎng)景問題# 1.1 復(fù)雜的獎(jiǎng)金計(jì)算## 考慮這樣一個(gè)實(shí)際應(yīng)用:就是如何實(shí)現(xiàn)靈活的獎(jiǎng)金計(jì)算。 獎(jiǎng)金計(jì)算是相對(duì)復(fù)雜...
    七寸知架構(gòu)閱讀 4,294評(píng)論 4 67
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,534評(píng)論 19 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,638評(píng)論 18 399
  • 一層繼承一層。裝飾模式是為已有功能動(dòng)態(tài)地添加更多功能的一種方式。關(guān)鍵詞是添加更多功能。 當(dāng)系統(tǒng)需要新功能時(shí),是向舊...
    愿成有心人閱讀 338評(píng)論 0 0
  • 1 場(chǎng)景問題# 1.1 商品類別樹## 考慮這樣一個(gè)實(shí)際的應(yīng)用:管理商品類別樹。 在實(shí)現(xiàn)跟商品有關(guān)的應(yīng)用系統(tǒng)的時(shí)候...
    七寸知架構(gòu)閱讀 6,260評(píng)論 10 59

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