需求
寫一個給人模擬搭配不同服飾的程序,可以給人換各種各樣的衣服褲子的形象。
初步實現
需求比較簡單,直接上代碼:
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("領帶 ");
}
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();
}
}
運行結果:
程序運行結果
分析
通過前面幾節的介紹,可以發現這種寫法的顯著毛病:難以實現功能拓展。比如現在不僅僅只有這兩種搭配方式,額外需要添加其他的搭配方式,該怎么辦呢?這種寫法必然需要對Person類進行手術,違反了開閉原則。
改進實現
很容易結合前面的實例,我們利用面向對象的程序設計方式,通過創造服飾抽象類,讓其他具體的西裝、領帶、垮褲等來繼承,實現程序功能的可擴展。
UML類圖:
UML類圖
Person類實現:
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();
}
其他具體衣服的實現,以大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();
}
}
再次分析
<<大話設計模式>>49頁中提及,上面這種改進方案雖然實現了服飾與人的分離,且利用面向對象程序設計的特性可以實現功能的擴展,但是仍然存在一個顯著的問題——穿衣服的動作是一步一步完全暴露在外面的。
xz.show();
ld.show();
px.show();
通過在客戶端,一步步的進行穿著服飾,這種設計方案是不夠良好的。(但實際開發中不好在哪,我也沒有深刻理解。)這些操作應該在內部組裝完畢,同時也要將需要的功能(服飾)按照一定的順序進行串聯,不能先打領帶后穿襯衫等...而裝飾模式就是解決這一需求的。
裝飾模式
- 定義:動態給一個對象添加一些額外的職責,使用Decorator模式相比用生成子類方式達到功能的擴充顯得更為靈活。
- 設計初衷:通??梢允褂美^承來實現功能的拓展,如果這些需要拓展的功能的種類很繁多,那么勢必生成很多子類,增加系統的復雜性,同時,使用繼承實現功能拓展,我們必須可預見這些拓展功能,這些功能是編譯時就確定了,是靜態的。
裝飾模式的使用
UML類圖:
UML類圖
這里需要注意的是,裝飾模式,裝飾類與被裝飾類都是繼承自同一父類,究竟是為什么?這一點在客戶端代碼中可以體現,需要好好體會,這也是理解裝飾模式的關鍵所在。
代碼實現:
-
Component抽象類——被裝飾者的抽象類,實際中也不一定有此類:
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(); } }
總結:
- 裝飾模式是用setComponent()方法來進行裝飾,其實也就是功能擴展;
- 每個裝飾類裝飾什么樣的功能和這個裝飾類如何被調用進行裝飾是分離的,即裝飾類A/B能完成什么樣的裝飾與他們什么時候被調用、按照什么順序是無關的。
利用裝飾模式實現換衣
UML類圖:
UML類圖
代碼實現:
-
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(); } }
-
運行結果:
運行結果
裝飾模式小結
- 裝飾模式是為已有功能動態的添加更多功能的一種方式;
- 裝飾模式把類中的裝飾功能從類中搬移去除,這樣可以簡化原有的類。同時有效的把類的核心職責和裝飾功能區分開,而且可以 去除相關類中重復的裝飾邏輯 ;(后半句話怎么理解呢?望評論出你的看法!)
- 當系統需要新功能時,是向舊的類中添加新的代碼。這些新的代碼通常裝飾了原有類的核心職責或主要行為。但這種做法的問題在于,他們在主類中加入了新的字段、新的方法和新的邏輯,從而增加了主類的復雜度,而這些新加入的東西僅僅是為了滿足一些只在某種特定情況下才會執行的特殊行為的需要。裝飾模式提供了一種非常好的解決方案,它把每個要裝飾的功能放在單獨的類中,并讓這個類包裝它所要裝飾的對象,因此,當需要執行特殊行為時,客戶代碼就可以根據需要、有選擇按順序的使用裝飾功能包裝對象;
- 對于前面所說為什么裝飾模式,被裝飾者與裝飾者都要繼承同一父類,這一點我覺得相當于給人穿了件衣服后返回的新對象畢竟本質還是人,只是多了件衣服,這樣就可以不斷的穿衣服不斷的返回。