結(jié)構(gòu)模式之Decorator——裝飾模式
1、簡(jiǎn)介裝飾模式(Decorator)也叫包裝器模式(Wrapper)。GOF在《設(shè)計(jì)模式》一書(shū)中給出的定義為:動(dòng)態(tài)地給一個(gè)對(duì)象添加一些額外的職責(zé)。就增加功能來(lái)說(shuō),Decorator模式相比生成子類更為靈活。請(qǐng)關(guān)注兩個(gè)重點(diǎn):
動(dòng)態(tài):也就是說(shuō)無(wú)法預(yù)先在編譯階段就知道職責(zé)的具體內(nèi)容和添加時(shí)機(jī),什么時(shí)候添加什么功能完全由客戶在運(yùn)行時(shí)刻規(guī)定。
增加:“Decorator模式”往往用于在現(xiàn)有功能的基礎(chǔ)上針對(duì)不同的對(duì)象,添加一些不同的功能,實(shí)際上我們還可以改變行為的流程。
2、使用場(chǎng)景及能解決的問(wèn)題
有時(shí)候在同一個(gè)運(yùn)行環(huán)境下,由于不同對(duì)象具有不同的特性導(dǎo)致了它們?cè)趫?zhí)行同一種類型操作時(shí),可能存在一些細(xì)小的差別,比如:參數(shù)不同、過(guò)程不同、方式不同等。
那么針對(duì)這三種情況我們可以采取的解決方法有兩種:
采用繼承的方式:定義一個(gè)父類的抽象方法,由不同的子類各自實(shí)現(xiàn)不同的操作,但一旦子類一多勢(shì)必帶來(lái)類的數(shù)量膨脹的問(wèn)題,難以管理;其次如果只是一些局部的細(xì)微差別而導(dǎo)致了必須重寫(xiě)整個(gè)過(guò)程或方法,那么必將導(dǎo)致出現(xiàn)大量的重復(fù)代碼(即功能相同部分的代碼)。有些項(xiàng)目,里面存在了大量的重復(fù)代碼,到處都是“Copy-Paste”,只是修改了某處的關(guān)鍵參數(shù)而已。
而“Duplicate code”正是《重構(gòu)-改善既有代碼的設(shè)計(jì)》一書(shū)中提到的首要“異味”,這意味著一旦公共部分的功能改變了,那么你有可能需要重新編譯幾個(gè)甚至十幾個(gè)子類。
采用“Decorator模式”:首先將功能分解,分成若干個(gè)小塊,這是為了后面我們可以動(dòng)態(tài)地重組。其次將功能部分抽取出來(lái)成為一個(gè)接口,通過(guò)一個(gè)實(shí)現(xiàn)了接口的基類完成基本的共有功能。一旦我們需要在基類的功能基礎(chǔ)上添加或改變流程,我們就可以通過(guò)“Decorator類”實(shí)現(xiàn)接口,在接口的實(shí)現(xiàn)方法中增加、改變方法的功能或流程。
比較上面這兩種做法,我們可以看到使用了“Decorator模式”后,代碼更加簡(jiǎn)潔了,可重用性和可擴(kuò)展性更高了。其次由于可以在構(gòu)造 “Decorator類”的過(guò)程中傳遞參數(shù),我們可以動(dòng)態(tài)的增加、改變?cè)械墓δ堋⒘鞒蹋嗤糠值墓δ軇t委托給基類去完成。
所以裝飾模式適用的場(chǎng)景包括:
需要擴(kuò)展一個(gè)類的功能,或給一個(gè)類增加附加責(zé)任。
需要?jiǎng)討B(tài)地給一個(gè)對(duì)象增加功能,這些功能可以再動(dòng)態(tài)地撤銷。
需要增加由一些基本功能的排列組合而產(chǎn)生的非常大量的功能,從而使繼承關(guān)系變得不現(xiàn)實(shí)。
3、類圖

4、實(shí)例
裝飾模式涉及到一下幾個(gè)角色:
* 抽象構(gòu)件(Component)角色:給出一個(gè)抽象接口,以規(guī)范準(zhǔn)備接收附加責(zé)任的對(duì)象。
* 具體構(gòu)件(Concrete Component)角色:定義一個(gè)將要接收附加責(zé)任的類。
* 裝飾(Decorator)角色:持有一個(gè)構(gòu)件(Component)對(duì)象的實(shí)例,并定義一個(gè)與抽象構(gòu)件接口一致的接口。
* 具體裝飾(Concrete Decorator)角色:負(fù)責(zé)給構(gòu)件對(duì)象"貼上"附加的責(zé)任。
下面分別看一下這幾個(gè)角色的實(shí)現(xiàn):
Component:
package decorator;
public interface Mobile {
void call();
void sendMessage();
}
Concrete Component:public interface Mobile {
void call();
void sendMessage();
}
package decorator;
public class MotoMobile implements Mobile {
public void call(){
System.out.print("call somebody with Moto");
}
public void sendMessage(){
System.out.print("send message to somebody with Moto");
}
}
package decorator;
public class NokiaMobile implements Mobile {
public void call(){
System.out.print("call somebody with Nokia");
}
public void sendMessage(){
System.out.print("send message to somebody with Nokia");
}
}
public class MotoMobile implements Mobile {
public void call(){
System.out.print("call somebody with Moto");
}
public void sendMessage(){
System.out.print("send message to somebody with Moto");
}
}
package decorator;
public class NokiaMobile implements Mobile {
public void call(){
System.out.print("call somebody with Nokia");
}
public void sendMessage(){
System.out.print("send message to somebody with Nokia");
}
}
Decorator:
package decorator;
public class DecoratorMobile implements Mobile {
private Mobile mobile;
public DecoratorMobile(Mobile mobile){
this.mobile=mobile;
}
public void call(){
this.mobile.call();
}
public void sendMessage(){
this.mobile.sendMessage();
}
}
public class DecoratorMobile implements Mobile {
private Mobile mobile;
public DecoratorMobile(Mobile mobile){
this.mobile=mobile;
}
public void call(){
this.mobile.call();
}
public void sendMessage(){
this.mobile.sendMessage();
}
}
Concrete Decorator:
package decorator;
public class MobileGPS extends DecoratorMobile {
public MobileGPS(Mobile mobile){
super(mobile);
}
public void call(){
super.call();
System.out.println(":use GPS");
}
public void sendMessage(){
super.sendMessage();
System.out.println(":use GPS");
}
}
package decorator;
public class MobileBlueTooth extends DecoratorMobile {
public MobileBlueTooth(Mobile mobile){
super(mobile);
}
public void call(){
super.call();
System.out.println(":use BlueTooth");
}
public void sendMessage(){
super.sendMessage();
System.out.println(":use BlueTooth");
}
}
最后看看調(diào)用的客戶端:public class MobileGPS extends DecoratorMobile {
public MobileGPS(Mobile mobile){
super(mobile);
}
public void call(){
super.call();
System.out.println(":use GPS");
}
public void sendMessage(){
super.sendMessage();
System.out.println(":use GPS");
}
}
package decorator;
public class MobileBlueTooth extends DecoratorMobile {
public MobileBlueTooth(Mobile mobile){
super(mobile);
}
public void call(){
super.call();
System.out.println(":use BlueTooth");
}
public void sendMessage(){
super.sendMessage();
System.out.println(":use BlueTooth");
}
}
package decorator;
public class Client {
private static Mobile mobile=new MotoMobile();
private static MobileGPS mobileGPS=new MobileGPS(mobile);
private static MobileBlueTooth mobileBlueTooth=new MobileBlueTooth(mobile);
public static void main(String args[]){
mobileGPS.call();
mobileGPS.sendMessage();
mobileBlueTooth.call();
mobileBlueTooth.sendMessage();
}
}
public class Client {
private static Mobile mobile=new MotoMobile();
private static MobileGPS mobileGPS=new MobileGPS(mobile);
private static MobileBlueTooth mobileBlueTooth=new MobileBlueTooth(mobile);
public static void main(String args[]){
mobileGPS.call();
mobileGPS.sendMessage();
mobileBlueTooth.call();
mobileBlueTooth.sendMessage();
}
}
5、優(yōu)缺點(diǎn)
比靜態(tài)繼承更靈活:與對(duì)象的靜態(tài)繼承相比,Decorator模式提供了更加靈活的向?qū)ο筇砑勇氊?zé)的方式,可以使用添加和分離的方法,用裝飾在運(yùn)行時(shí)刻增加和刪除職責(zé)。
避免在層次結(jié)構(gòu)高層的類有太多的特征:Decorator模式提供了一種“即插即用”的方法來(lái)添加職責(zé),他并不試圖在一個(gè)復(fù)雜的可訂制的類中支持所有可預(yù)見(jiàn)的特征,相反可以定義一個(gè)簡(jiǎn)單的類,并且用Decorator類給他逐漸的添加功能,可以從簡(jiǎn)單的部件組合出復(fù)雜的功能。
會(huì)產(chǎn)生比使用繼承關(guān)系更多的對(duì)象,使得維護(hù)和排查問(wèn)題略顯復(fù)雜。
6、Decorator模式與Adapater、Builder模式的區(qū)別
Adapter和Decorator模式的區(qū)別:
前者關(guān)注的是如何將兩個(gè)本來(lái)互不相關(guān)的但又有相似功能的類糅合到一起,后者關(guān)注的是如何在原來(lái)功能的基礎(chǔ)上做一些小的調(diào)整。
Builder和Decorator模式的區(qū)別:
前者關(guān)注的是對(duì)象的構(gòu)建,后者關(guān)注的是對(duì)象功能的添加、個(gè)性化。
前者主要用于GUI界面構(gòu)建對(duì)象的場(chǎng)合、后者主要用于動(dòng)態(tài)添加功能、改變流程的場(chǎng)合。
posted on 2008-06-25 13:33 云淡風(fēng)清 閱讀(356) 評(píng)論(0) 編輯 收藏 所屬分類: Design Patterns