每當(dāng)我們買了新房子之后,相信絕大部分人都會進行裝修,給房子增加一些其他新的物品。不過,無論如何裝修,這個房子還是這個房子,最本質(zhì)的東西并沒有變,有的只是我們通過裝修的方式,給這個房子增加了一些額外的功能.....
通過裝修的方式,給房子增加一些我們想要的額外功能,這種方式,就類似于我們今天要講的設(shè)計模式:裝飾者模式。
1.裝飾模式概念
裝飾模式可以在不改變一個對象本身功能的基礎(chǔ)上給對象增加額外的新行為。例如上面的房子裝修,我們買了手機之后給手機裝上手機殼,貼上手機膜等。這些,都可以說是裝修模式的應(yīng)用。
不過這里需要注意的是,裝修模式與繼承不一樣,繼承是需要一個子類來拓展對象的功能,而裝飾模式則不一樣,它是通過定義一個裝飾類,在這個裝飾類中持有某些對象的引用,然后通過使用對象之間的關(guān)聯(lián)關(guān)系來取代類之間的繼承關(guān)系。
2.定義
裝飾模式(Decorator Pattern):在不必改變原類文件和原類使用的繼承的情況下,動態(tài)地擴展一個對象的功能。就增加對象功能來說,裝飾模式比生成子類實現(xiàn)更為靈活。裝飾模式是一種對象結(jié)構(gòu)型模式。
角色
和觀察者模式類似,觀察者模式一般會有如下四個角色:
- 抽象構(gòu)件(Component).
- 具體構(gòu)件(ConcreteComponet)。
- 抽象裝飾類(Decorator)。
- 具體裝飾類(ConcreteDecorator)。
下面介紹下四個角色的一些功能、任務(wù)。
- 抽象構(gòu)件:它是具體構(gòu)件和抽象裝飾類的共同父類,聲明了要在具體構(gòu)件中實現(xiàn)的業(yè)務(wù)方法,它的引入可以使客戶端以一致的方式處理未被裝飾的對象以及裝飾之后的對象。抽象構(gòu)件一般定義為接口。
- 具體構(gòu)件:它是抽象構(gòu)件類的子類,用于定義具體的構(gòu)件對象,實現(xiàn)了在抽象構(gòu)件中聲明的方法,裝飾對象可以給它增加額外的職責(zé)(方法)。
- 抽象裝飾類:它也是抽象構(gòu)件類的子類,用于給具體構(gòu)件增加職責(zé),但是具體職責(zé)在其子類中實現(xiàn)。它維護一個指向抽象構(gòu)件對象的引用,通過該引用可以調(diào)用裝飾之前構(gòu)件對象的方法,并通過其子類擴展該方法,以達到裝飾的目的。
- 具體裝飾類:它是抽象裝飾類的子類,負(fù)責(zé)向構(gòu)件添加新的職責(zé)。每一個具體裝飾類都定義了一些新的行為,它可以調(diào)用在抽象裝飾類中定義的方法,并可以增加新的方法用以擴充對象的行為。
下面我給個例子吧,看個例子就比較知道這些角色都是個什么情況了。
3.例子
在n年前,人類捕捉了動物之后,會直接殺了之后就拿來吃,而不會把它烤熟、弄上一些配料之后再吃。但是到了后來,人類就懂得了把動物烤熟、弄上配料之后再吃了。
現(xiàn)在我們假設(shè)人類本來是不會烤熟之后再吃的,而是被裝飾了一些額外的能力之后,才知道烤熟之后再吃。(貌似這個例子有點勉強.....)。
現(xiàn)在開始定義上面那四個角色:
1.抽象構(gòu)件
/**
* 定義作為人類應(yīng)有的一些規(guī)范
*/
public interface Human {
void eating();
}
2.具體構(gòu)件
/**
* 具體構(gòu)件類
*/
public class Man implements Human{
@Override
public void eating() {
System.out.println("吃剛剛捕獲的美味佳肴");
}
}
3.抽象裝飾類
/**
* 抽象裝飾者,需要實現(xiàn)抽象構(gòu)件接口
*/
public class Decorator implements Human{
//維持對抽象構(gòu)件的引用
private Human human;
//注入構(gòu)件實例
public Decorator(Human human) {
this.human = human;
}
@Override
public void eating() {
human.eating();
}
}
注意: 在Decorator中并未真正實現(xiàn)eat()方法,而只是調(diào)用原有Human對象的eating()方法,它沒有真正實施裝飾,而是提供一個統(tǒng)一的接口,將具體裝飾過程交給子類完成。
4.具體裝飾類
public class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Human human){
super(human);
}
@Override
public void eating() {
System.out.println("把捕捉的動物給烤熟了,在加些配料");
super.eating();
System.out.println("吃完飯之后洗手");
}
//需要增加的其他方法
}
5.測試類
public class Demo {
public static void main(String[] args){
Human man = new Man();
//沒進行裝飾之前
man.eating();
System.out.println("--------------------");
//進行裝飾之后
man = new ConcreteDecorator(man);
man.eating();
}
}
6.打印結(jié)果
吃剛剛捕獲的美味佳肴
--------------------
把捕捉的動物給烤熟了,在加些配料
吃剛剛捕獲的美味佳肴
吃完飯之后洗手
從這個簡單的例子可以看到,沒有并沒有改變原來的Man類,同時也沒有定義他的子類來進行功能的拓展,而是通過一個裝飾類的輔助來實現(xiàn)Man類功能的增強,這就是裝飾者模式的作用。
優(yōu)缺點
優(yōu)點:
1,使用裝飾者模式比使用繼承更加靈活,因為它選擇通過一種動態(tài)的方式來擴展一個對象的功能,在運行時可以選擇不同的裝飾器,從而實現(xiàn)不同的行為。
2,通過使用不同的具體裝飾類以及這些裝飾類的排列組合,可以創(chuàng)造出很多不同行為的組合??梢允褂枚鄠€具體裝飾類來裝飾同一對象,得到功能更為強大的對象。
3,具體構(gòu)件類與具體裝飾類可以獨立變化,他能是低耦合的。用戶可以根據(jù)需要來增加新的具體構(gòu)件類和具體裝飾類,在使用時再對其進行各種組合,原有代碼無須改變,符合“開閉原則”。
缺點:
1,會產(chǎn)生很多的小對象,增加了系統(tǒng)的復(fù)雜性
2,由于使用裝飾模式,可以比使用繼承關(guān)系需要較少數(shù)目的類。使用較少的類,當(dāng)然使設(shè)計比較易于進行。但是,在另一方面,使用裝飾模式會產(chǎn)生比使用繼承關(guān)系更多的對象。更多的對象會使得查錯變得困難,特別是這些對象看上去都很相像。
參考資料:
- 設(shè)計模式j(luò)ava版。
- Head First設(shè)計模式
完
關(guān)注公我的眾號:苦逼的碼農(nóng),獲取更多原創(chuàng)文章,后臺回復(fù)禮包送你一份時下熱門的資源大禮包。同時也感謝把文章介紹給更多需要的人