模式設計——裝飾模式

模式設計——裝飾模式

裝飾模式又名包裝(Wrapper)模式。裝飾模式以對客戶端透明的方式擴展對象的功能,是繼承關係的一個替代方案。app

1、裝飾模式的結構

裝飾模式以對客戶透明的方式動態地給一個對象附加上更多的責任。換言之,客戶端並不會以爲對象在裝飾前和裝飾後有什麼不一樣。裝飾模式能夠在不使用創造更多子類的狀況下,將對象的功能加以擴展。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();

// 寫相關的業務代碼

}

}

2、實例

孫悟空有七十二般變化,他的每一種變化都給他帶來一種附加的本領。他變成魚兒時,就能夠到水裏游泳;他變成鳥兒時,就能夠在天上飛行。

本例中,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類,而「鳥兒」、「魚兒」是裝飾類。要裝飾的是「大聖本尊」,也即「猢猻」實例。

上面的例子中,系統把大聖從一隻猢猻裝飾成了一隻鳥兒(把鳥兒的功能加到了猢猻身上),而後又把鳥兒裝飾成了一條魚兒(把魚兒的功能加到了猢猻+鳥兒身上,獲得了猢猻+鳥兒+魚兒)。

 

如上圖所示,大聖的變化首先將鳥兒的功能附加到了猢猻身上,而後又將魚兒的功能附加到猢猻+鳥兒身上。

3、裝飾模式的簡化

大多數狀況下,裝飾模式的實現都要比上面給出的示意性例子要簡單。

若是隻有一個ConcreteComponent類,那麼能夠考慮去掉抽象的Component類(接口),把Decorator做爲一個ConcreteComponent子類。以下圖所示:

 

若是隻有一個ConcreteDecorator類,那麼就沒有必要創建一個單獨的Decorator類,而能夠把DecoratorConcreteDecorator的責任合併成一個類。甚至在只有兩個ConcreteDecorator類的狀況下,均可以這樣作。以下圖所示:

 

4、透明性的要求

裝飾模式對客戶端的透明性要求程序不要聲明一個ConcreteComponent類型的變量,而應當聲明一個Component類型的變量。

用孫悟空的例子來講,必須永遠把孫悟空的全部變化都當成孫悟空來對待,而若是把老孫變成的魚兒當成魚兒,而不是老孫,那就被老孫騙了,而這時不該當發生的。下面的作法是對的:

TheGreatestSage sage = new Monkey();

TheGreatestSage bird = new Bird(sage);

而下面的作法是不對的:

Monkey sage = new Monkey();

Bird bird = new Bird(sage);

5、半透明的裝飾模式

然而,純粹的裝飾模式很難找到。裝飾模式的用意是在不改變接口的前提下,加強所考慮的類的性能。在加強性能的時候,每每須要創建新的公開的方法。即使是在孫大聖的系統裏,也須要新的方法。好比齊天大聖類並無飛行的能力,而鳥兒有。這就意味着鳥兒應當有一個新的fly()方法。再好比,齊天大聖類並無游泳的能力,而魚兒有,這就意味着在魚兒類裏應當有一個新的swim()方法。

這就致使了大多數的裝飾模式的實現都是「半透明」的,而不是徹底透明的。換言之,容許裝飾模式改變接口,增長新的方法。這意味着客戶端能夠聲明ConcreteDecorator類型的變量,從而能夠調用ConcreteDecorator類中才有的方法:

TheGreatestSage sage = new Monkey();

Bird bird = new Bird(sage);

bird.fly();

半透明的裝飾模式是介於裝飾模式和適配器模式之間的。適配器模式的用意是改變所考慮的類的接口,也能夠經過改寫一個或幾個方法,或增長新的方法來加強或改變所考慮的類的功能。大多數的裝飾模式其實是半透明的裝飾模式,這樣的裝飾模式也稱作半裝飾、半適配器模式。

6、裝飾模式的優勢

1)裝飾模式與繼承關係的目的都是要擴展對象的功能,可是裝飾模式能夠提供比繼承更多的靈活性。裝飾模式容許系統動態決定「貼上」一個須要的「裝飾」,或者除掉一個不須要的「裝飾」。繼承關係則不一樣,繼承關係是靜態的,它在系統運行前就決定了。

2)經過使用不一樣的具體裝飾類以及這些裝飾類的排列組合,設計師能夠創造出不少不一樣行爲的組合。

7、裝飾模式的缺點

因爲使用裝飾模式,能夠比使用繼承關係須要較少數目的類。使用較少的類,固然使設計比較易於進行。可是,在另外一方面,使用裝飾模式會產生比使用繼承關係更多的對象。更多的對象會使得查錯變得困難,特別是這些對象看上去都很相像。

相關文章
相關標籤/搜索