需求
寫一個(gè)給人模擬搭配不同服飾的程序宏所,可以給人換各種各樣的衣服褲子的形象雕蔽。
初步實(shí)現(xià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é)果:
分析
通過前面幾節(jié)的介紹声离,可以發(fā)現(xiàn)這種寫法的顯著毛病:難以實(shí)現(xiàn)功能拓展芒炼。比如現(xiàn)在不僅僅只有這兩種搭配方式,額外需要添加其他的搭配方式术徊,該怎么辦呢本刽?這種寫法必然需要對Person類進(jìn)行手術(shù),違反了開閉原則赠涮。
改進(jìn)實(shí)現(xiàn)
很容易結(jié)合前面的實(shí)例子寓,我們利用面向?qū)ο蟮某绦蛟O(shè)計(jì)方式,通過創(chuàng)造服飾抽象類笋除,讓其他具體的西裝斜友、領(lǐng)帶、垮褲等來繼承垃它,實(shí)現(xiàn)程序功能的可擴(kuò)展鲜屏。
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頁中提及洛史,上面這種改進(jìn)方案雖然實(shí)現(xiàn)了服飾與人的分離,且利用面向?qū)ο蟪绦蛟O(shè)計(jì)的特性可以實(shí)現(xiàn)功能的擴(kuò)展酱吝,但是仍然存在一個(gè)顯著的問題——穿衣服的動作是一步一步完全暴露在外面的也殖。
xz.show();
ld.show();
px.show();
通過在客戶端,一步步的進(jìn)行穿著服飾务热,這種設(shè)計(jì)方案是不夠良好的忆嗜。(但實(shí)際開發(fā)中不好在哪,我也沒有深刻理解崎岂。)這些操作應(yīng)該在內(nèi)部組裝完畢饮焦,同時(shí)也要將需要的功能(服飾)按照一定的順序進(jìn)行串聯(lián)抡柿,不能先打領(lǐng)帶后穿襯衫等...而裝飾模式就是解決這一需求的倔毙。
裝飾模式
- 定義:動態(tài)給一個(gè)對象添加一些額外的職責(zé),使用Decorator模式相比用生成子類方式達(dá)到功能的擴(kuò)充顯得更為靈活侮穿。
- 設(shè)計(jì)初衷:通诚煳剑可以使用繼承來實(shí)現(xiàn)功能的拓展,如果這些需要拓展的功能的種類很繁多,那么勢必生成很多子類,增加系統(tǒng)的復(fù)雜性,同時(shí),使用繼承實(shí)現(xiàn)功能拓展,我們必須可預(yù)見這些拓展功能,這些功能是編譯時(shí)就確定了,是靜態(tài)的。
裝飾模式的使用
UML類圖:
這里需要注意的是省艳,裝飾模式娘纷,裝飾類與被裝飾類都是繼承自同一父類,究竟是為什么跋炕?這一點(diǎn)在客戶端代碼中可以體現(xiàn)赖晶,需要好好體會,這也是理解裝飾模式的關(guān)鍵所在辐烂。
代碼實(shí)現(xiàn):
-
Component抽象類——被裝飾者的抽象類遏插,實(shí)際中也不一定有此類:
public abstract class Component { public abstract void meathod(); }
-
具體的被裝飾者類:
public class ConcreteComponent extends Component { @Override public void meathod() { System.out.println("具體要被裝飾的對象的方法"); } }
-
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(); } }
-
具體的裝飾類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(); } }
-
客戶端類:
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類圖:
代碼實(shí)現(xiàn):
-
Person類——被裝飾者;
public class Person { private String name; public void setName(String name) { this.name = name; } public void show() { System.out.println("裝扮的" + name); } }
-
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(); } }
-
其他具體裝飾類鹰祸;
public class BigTrousers extends Finery { @Override public void show() { super.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.setPerson(person); ld.setPerson(xz); px.setPerson(ld); px.show(); } }
-
運(yùn)行結(jié)果:
裝飾模式小結(jié)
- 裝飾模式是為已有功能動態(tài)的添加更多功能的一種方式密浑;
- 裝飾模式把類中的裝飾功能從類中搬移去除蛙婴,這樣可以簡化原有的類。同時(shí)有效的把類的核心職責(zé)和裝飾功能區(qū)分開尔破,而且可以 去除相關(guān)類中重復(fù)的裝飾邏輯 街图;(后半句話怎么理解呢?望評論出你的看法!)
- 當(dāng)系統(tǒng)需要新功能時(shí)懒构,是向舊的類中添加新的代碼台夺。這些新的代碼通常裝飾了原有類的核心職責(zé)或主要行為。但這種做法的問題在于痴脾,他們在主類中加入了新的字段、新的方法和新的邏輯梳星,從而增加了主類的復(fù)雜度赞赖,而這些新加入的東西僅僅是為了滿足一些只在某種特定情況下才會執(zhí)行的特殊行為的需要。裝飾模式提供了一種非常好的解決方案冤灾,它把每個(gè)要裝飾的功能放在單獨(dú)的類中前域,并讓這個(gè)類包裝它所要裝飾的對象,因此韵吨,當(dāng)需要執(zhí)行特殊行為時(shí)匿垄,客戶代碼就可以根據(jù)需要、有選擇按順序的使用裝飾功能包裝對象;
- 對于前面所說為什么裝飾模式椿疗,被裝飾者與裝飾者都要繼承同一父類漏峰,這一點(diǎn)我覺得相當(dāng)于給人穿了件衣服后返回的新對象畢竟本質(zhì)還是人,只是多了件衣服届榄,這樣就可以不斷的穿衣服不斷的返回浅乔。