源代碼:https://gitee.com/AgentXiao/DecroatorPattern
要點(diǎn):
1、裝飾模式解決的問題
2、裝飾模式的各個模塊實(shí)現(xiàn)
3、裝飾模式的優(yōu)缺點(diǎn)
4、裝飾模式和橋接模式的區(qū)別
一、場景引入

如上圖所示,有一個狗(Dog)類,下面有三個子類,分別是會飛的狗(FlyDog)、會游泳的狗(SwimDog)、會跑的狗(RunDog)。假設(shè)此時三個子類都是單一功能的,也就說FlyDog只會飛,游泳和跑步都不會。
如果此時需要一個既會飛也會跑的狗,我們可以通過繼承的方式實(shí)現(xiàn),一次類推,可以得到:

這顯然不是最好的方法!當(dāng)功能非常多時,需要繼承父類的子類數(shù)量將會大大增加!而裝飾模式就是解決這個問題的一種設(shè)計模式。
二、裝飾模式
裝飾模式是一種用于代替繼承的技術(shù),無需通過繼承增加子類就能夠擴(kuò)展對象的新功能。使用對象的組合關(guān)系(區(qū)分于“組合設(shè)計模式”)代替繼承關(guān)系,避免類型體系的快速膨脹。
簡單地說,裝飾模式用于動態(tài)增加一個對象的新功能,或成為功能增強(qiáng)。
裝飾模式實(shí)現(xiàn)細(xì)節(jié):

(1)抽象構(gòu)建角色Component。這是真實(shí)對象和裝飾對象都需要實(shí)現(xiàn)的接口,便于客戶端可以使用相同的方式交互裝飾對象和真實(shí)對象,指上文的Dog。
(2)真實(shí)對象ConcreteComponent。具體需要裝飾的對象,定義為MyDog。
(3)裝飾對象Decorator。持有一個抽象構(gòu)件的引用(核心)。裝飾對象接受所有客戶端的請求,并把這些請求通過引用轉(zhuǎn)發(fā)給真實(shí)對象。這樣,就能在真實(shí)對象調(diào)用前后增加新的功能,定義為SuperDog。
(4)具體裝飾對象ConcreteDecorator:指上文的Fly、Swim、Run。
三、裝飾模式的實(shí)現(xiàn)
1、抽象角色
/**
* @InterfaceName Dog
* @Description 抽象構(gòu)建角色
* @Author xwd
* @Date 2018/10/25 9:52
*/
public interface Dog {
/**
* @MethodName showPower
* @Descrition 展示能力
* @Param []
* @return void
*/
void showPower();
}
2、具體角色
/**
* @ClassName MyDog
* @Description 具體對象
* @Author xwd
* @Date 2018/10/25 9:53
*/
public class MyDog implements Dog{
@Override
public void showPower() {
System.out.println("我還沒有什么能力!");
}
}
3、裝飾角色(核心)
/**
* @ClassName SuperDog
* @Description 裝飾對象
* @Author xwd
* @Date 2018/10/25 9:54
*/
public class SuperDog implements Dog{
protected Dog dog;//持有抽象構(gòu)建角色的引用
public SuperDog(Dog dog) {
this.dog = dog;
}
@Override
public void showPower() {
dog.showPower();
}
}
4、具體的裝飾角色
/**
* @ClassName Fly
* @Description 飛行能力
* @Author xwd
* @Date 2018/10/25 9:57
*/
public class Fly extends SuperDog{
public Fly(Dog dog) {
super(dog);
}
@Override
public void showPower() {
super.showPower();
System.out.println("我具備飛行功能!");
}
}
/**
* @ClassName Run
* @Description 奔跑能力
* @Author xwd
* @Date 2018/10/25 9:57
*/
public class Run extends SuperDog{
public Run(Dog dog) {
super(dog);
}
@Override
public void showPower() {
super.showPower();
System.out.println("我具備奔跑功能!");
}
}
/**
* @ClassName Swim
* @Description 游泳能力
* @Author xwd
* @Date 2018/10/25 9:57
*/
public class Swim extends SuperDog{
public Swim(Dog dog) {
super(dog);
}
@Override
public void showPower() {
super.showPower();
System.out.println("我具備游泳功能!");
}
}
5、測試
/**
* @ClassName Client
* @Description 測試裝飾模式
* @Author xwd
* @Date 2018/10/25 9:59
*/
public class Client {
public static void main(String[] args) {
//原生態(tài)的狗,還沒有什么能力
Dog dog = new MyDog();
dog.showPower();
System.out.println("*********************");
//添加了裝飾模式的狗,但是還沒有添加具體功能
SuperDog superDog = new SuperDog(dog);
superDog.showPower();
System.out.println("*********************");
//添加了飛行能力的狗
SuperDog flyDog = new Fly(dog);
flyDog.showPower();
System.out.println("*********************");
//添加了游泳能力的狗
SuperDog swimDog = new Swim(dog);
swimDog.showPower();
System.out.println("*********************");
//既添加了飛行能力又添加了奔跑能力的狗
SuperDog runDog = new Run(dog);
SuperDog fsDog = new Fly(runDog);
fsDog.showPower();
System.out.println("*********************");
}
}
6、測試結(jié)果

由此可見,需要為一個對象添加新功能時,只需要建立這個功能,將具體對象傳入。如果傳入的對象是已經(jīng)具備某些功能的,就相當(dāng)遠(yuǎn)在那個基礎(chǔ)上添加新的功能。
四、開發(fā)中使用的場景
- IO中輸入流和輸出流的設(shè)計。
- Swing包中圖形界面構(gòu)件功能。
- Servlet API 中提供了一個request對象的Decorator設(shè)計模式的默認(rèn)實(shí)現(xiàn)類HttpServletRequestWrapper,HttpServletRequestWrapper類,增強(qiáng)了request對象的功能。
- Struts2中,request,response,session對象的處理。
在這里只解釋其中一種:
在IO流中,抽象構(gòu)建對象是InputStream、OutputStream、Reader、Writer。
真實(shí)對象是FileInputStream、FileOutputStream。

裝飾對象是FilterInputStream、FilterOutputStream。

具體的裝飾對象:BufferedOutputStream、BufferedInputStream等。


五、總結(jié)
1、功能
裝飾模式(Decorator)也叫包裝器模式(Wrapper)。
裝飾模式降低系統(tǒng)的耦合度,可以動態(tài)的增加或刪除對象的職責(zé),并使得需要裝飾的具體構(gòu)建類和具體裝飾類可以獨(dú)立變化,以便增加新的具體構(gòu)建類和具體裝飾類。
2、優(yōu)點(diǎn)
- 擴(kuò)展對象功能,比繼承靈活,不會導(dǎo)致類個數(shù)急劇增加。
- 可以對一個對象進(jìn)行多次裝飾,創(chuàng)造出不同行為的組合,得到功能更加強(qiáng)大的對象。
- 具體構(gòu)建類和具體裝飾類可以獨(dú)立變化,用戶可以根據(jù)需要自己增加新的具體構(gòu)件子類和具體裝飾子類。
3、缺點(diǎn)
- 產(chǎn)生很多小對象。大量小對象占據(jù)內(nèi)存,一定程度上影響性能。
- 裝飾模式易于出錯,調(diào)試排查比較麻煩。
4、裝飾模式和橋接模式的區(qū)別
兩個模式都是為了解決過多子類對象問題。但他們的誘因不一樣。
- 橋接模式是對象自身現(xiàn)有機(jī)制沿著多個維度變化。
- 裝飾模式是為了增加新的功能。