裝飾模式又名包裝(Wrapper)模式。裝飾模式以對客戶端透明的方式擴展對象的功能,是繼承關係的一個替代方案。app
裝飾模式以對客戶透明的方式動態地給一個對象附加上更多的責任。換言之,客戶端並不會以爲對象在裝飾前和裝飾後有什麼不一樣。裝飾模式能夠在不使用創造更多子類的狀況下,將對象的功能加以擴展。ide
裝飾模式的類圖以下:性能
在裝飾模式中的角色有:this
●抽象構件(Component)角色:給出一個抽象接口,以規範準備接收附加責任的對象。spa
●具體構件(ConcreteComponent)角色:定義一個將要接收附加責任的類。設計
●裝飾(Decorator)角色:持有一個構件(Component)對象的實例,並定義一個與抽象構件接口一致的接口。component
●具體裝飾(ConcreteDecorator)角色:負責給構件對象「貼上」附加的責任。對象
源代碼繼承
抽象構件角色接口
public interface Component {
public void sampleOperation();
}
具體構件角色
public class ConcreteComponent implements Component {
@Override
public void sampleOperation() {
// 寫相關的業務代碼
}
}
裝飾角色
public class Decorator implements Component{
private Component component;
public Decorator(Component component){
this.component = component;
}
@Override
public void sampleOperation() {
// 委派給構件
component.sampleOperation();
}
}
具體裝飾角色
public class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void sampleOperation() {
super.sampleOperation();
// 寫相關的業務代碼
}
}
public class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Component component) {
super(component);
}
@Override
public void sampleOperation() {
super.sampleOperation();
// 寫相關的業務代碼
}
}
孫悟空有七十二般變化,他的每一種變化都給他帶來一種附加的本領。他變成魚兒時,就能夠到水裏游泳;他變成鳥兒時,就能夠在天上飛行。
本例中,Component的角色便由鼎鼎大名的齊天大聖扮演;ConcreteComponent的角色屬於大聖的本尊,就是猢猻本人;Decorator的角色由大聖的七十二變扮演。而ConcreteDecorator的角色即是魚兒、鳥兒等七十二般變化。
源代碼
抽象構件角色「齊天大聖」接口定義了一個move()方法,這是全部的具體構件類和裝飾類必須實現的。
//大聖的尊號
public interface TheGreatestSage {
public void move();
}
具體構件角色「大聖本尊」猢猻類
public class Monkey implements TheGreatestSage {
@Override
public void move() {
//代碼
System.out.println("Monkey Move");
}
}
抽象裝飾角色「七十二變」
public class Change implements TheGreatestSage {
private TheGreatestSage sage;
public Change(TheGreatestSage sage){
this.sage = sage;
}
@Override
public void move() {
// 代碼
sage.move();
}
}
具體裝飾角色「魚兒」
public class Fish extends Change {
public Fish(TheGreatestSage sage) {
super(sage);
}
@Override
public void move() {
// 代碼
System.out.println("Fish Move");
}
}
具體裝飾角色「鳥兒」
public class Bird extends Change {
public Bird(TheGreatestSage sage) {
super(sage);
}
@Override
public void move() {
// 代碼
System.out.println("Bird Move");
}
}
客戶端類
public class Client {
public static void main(String[] args) {
TheGreatestSage sage = new Monkey();
// 第一種寫法
TheGreatestSage bird = new Bird(sage);
TheGreatestSage fish = new Fish(bird);
// 第二種寫法
//TheGreatestSage fish = new Fish(new Bird(sage));
fish.move();
}
}
「大聖本尊」是ConcreteComponent類,而「鳥兒」、「魚兒」是裝飾類。要裝飾的是「大聖本尊」,也即「猢猻」實例。
上面的例子中,系統把大聖從一隻猢猻裝飾成了一隻鳥兒(把鳥兒的功能加到了猢猻身上),而後又把鳥兒裝飾成了一條魚兒(把魚兒的功能加到了猢猻+鳥兒身上,獲得了猢猻+鳥兒+魚兒)。
如上圖所示,大聖的變化首先將鳥兒的功能附加到了猢猻身上,而後又將魚兒的功能附加到猢猻+鳥兒身上。
大多數狀況下,裝飾模式的實現都要比上面給出的示意性例子要簡單。
若是隻有一個ConcreteComponent類,那麼能夠考慮去掉抽象的Component類(接口),把Decorator做爲一個ConcreteComponent子類。以下圖所示:
若是隻有一個ConcreteDecorator類,那麼就沒有必要創建一個單獨的Decorator類,而能夠把Decorator和ConcreteDecorator的責任合併成一個類。甚至在只有兩個ConcreteDecorator類的狀況下,均可以這樣作。以下圖所示:
裝飾模式對客戶端的透明性要求程序不要聲明一個ConcreteComponent類型的變量,而應當聲明一個Component類型的變量。
用孫悟空的例子來講,必須永遠把孫悟空的全部變化都當成孫悟空來對待,而若是把老孫變成的魚兒當成魚兒,而不是老孫,那就被老孫騙了,而這時不該當發生的。下面的作法是對的:
TheGreatestSage sage = new Monkey();
TheGreatestSage bird = new Bird(sage);
而下面的作法是不對的:
Monkey sage = new Monkey();
Bird bird = new Bird(sage);
然而,純粹的裝飾模式很難找到。裝飾模式的用意是在不改變接口的前提下,加強所考慮的類的性能。在加強性能的時候,每每須要創建新的公開的方法。即使是在孫大聖的系統裏,也須要新的方法。好比齊天大聖類並無飛行的能力,而鳥兒有。這就意味着鳥兒應當有一個新的fly()方法。再好比,齊天大聖類並無游泳的能力,而魚兒有,這就意味着在魚兒類裏應當有一個新的swim()方法。
這就致使了大多數的裝飾模式的實現都是「半透明」的,而不是徹底透明的。換言之,容許裝飾模式改變接口,增長新的方法。這意味着客戶端能夠聲明ConcreteDecorator類型的變量,從而能夠調用ConcreteDecorator類中才有的方法:
TheGreatestSage sage = new Monkey();
Bird bird = new Bird(sage);
bird.fly();
半透明的裝飾模式是介於裝飾模式和適配器模式之間的。適配器模式的用意是改變所考慮的類的接口,也能夠經過改寫一個或幾個方法,或增長新的方法來加強或改變所考慮的類的功能。大多數的裝飾模式其實是半透明的裝飾模式,這樣的裝飾模式也稱作半裝飾、半適配器模式。
(1)裝飾模式與繼承關係的目的都是要擴展對象的功能,可是裝飾模式能夠提供比繼承更多的靈活性。裝飾模式容許系統動態決定「貼上」一個須要的「裝飾」,或者除掉一個不須要的「裝飾」。繼承關係則不一樣,繼承關係是靜態的,它在系統運行前就決定了。
(2)經過使用不一樣的具體裝飾類以及這些裝飾類的排列組合,設計師能夠創造出不少不一樣行爲的組合。
因爲使用裝飾模式,能夠比使用繼承關係須要較少數目的類。使用較少的類,固然使設計比較易於進行。可是,在另外一方面,使用裝飾模式會產生比使用繼承關係更多的對象。更多的對象會使得查錯變得困難,特別是這些對象看上去都很相像。