裝飾模式(Decorator)簡介

裝飾模式是第三個介紹的模式了.

這個模式沒有前面兩個那麼好理解.,


一, 裝飾模式(decorator)的定義.

教材裏是這樣寫的:

動態第給1個對象添加1寫額外的職責(功能), 就增加的功能來講, 裝飾模式比生成子類更加靈活.


就咁睇的確很難明白. 但我們起碼可以知道一下兩點:

1. 裝飾模式的作用是增強1個對象(類) 的功能.

2. 它比生成子類更加靈活.


本文着重講下第2點, 包括什麼是生成子類,  爲什麼裝飾模式比它更加靈活.



二, 1個Hunter1(獵人)類的例子.

舉1個很簡單的例子. 1個獵人類實現1個打獵的接口.


Huntable 接口:

public interface Huntable {
	public void hunt();
}

獵人類Hunter1:

public class Hunter1 implements Huntable{
	public void eat(){
		System.out.println("eating!");
	}
	
	public void hunt(){
		System.out.println("Hunter is Hunting!");
	}
}


可見Hunter1這個類實現了Huntable藉口. 

hunt()方法裏只簡單重寫了hunt()方法.


客戶端代碼:

Hunter1 h = new Hunter1();
h.hunt();

輸出:

Hunter is Hunting!



1個增強hunt()方法的需求:

好了, 現在有1個獵人, 他出去不只是打獵,  他打獵的同時還同時訓練它的獵狗, 最後還會給戰利品拍一張照片.

也就是需要增強hunt()方法功能.


加上

trainDog() 和 takePic()這個兩個方法.


怎麼實現呢?


三, 通過添加子類Hunter2方法來增強Hunter1

open-closed principle:

可能有人會覺得. 直接在Hunter1里加上兩個方法trainDog () 和  takePic(),  在hunt()裏調用這個兩個新方法不就完事了嗎.


的確, 但是這種做法修改了原有的類,  違反了"封閉-開放原則". 

所謂"封閉-開發原則" 就是對修改封閉, 對擴展開放.


在項目中, 很多舊的業務類根本不會讓你修改(jar包發佈或者你沒有權限)


所以我們應該把這個新需求當做1個擴展來處理, 而不是修改舊代碼.


其中1個方法就是增加子類.



新增1個Hunter2 類:

public class Hunter2 extends Hunter1{
	@Override
	public void hunt(){
		super.hunt();
		this.trainDog();
		this.takePic();
	}
	
	//new function()
	public void trainDog(){
		System.out.println("Train the Dog!");
	}
	
	public void takePic(){
		System.out.println("Take a picture!");
	}
}



客戶端代碼:

Hunter2 h2 = new Hunter2();
h2.hunt();


UML:



可見, 這種方法, 只是新增了1個子類, 以及修改客戶端代碼, 對原來的類代碼沒有任何修改, 是符合"封閉-修改 原則"的.



這種修改, 增強了原來的類Hunter1 的hunt()功能, 可以說是"裝飾"了原來的hunt()方法.

但是這種修改不是很靈活.


比如:   這種獵人是先打獵,再訓狗,再拍照的.

            如果有一種獵人喜歡先拍照再訓狗了,  我們就又要增加1個Hunter1的子類了....



而裝飾模式能解決問題.


四, 通過裝飾模式來增強Hunter類.

Huntable 接口 和 Hunter類:

首先構造兩個相同的接口和類:

public interface Huntable {
	public void hunt();
}

public class Hunter implements Huntable {
	public void eat(){
		System.out.println("eating!");
	}

	@Override
	public void hunt(){
		System.out.println("Hunter is Hunting!");
	}
}

Hunter類跟上面的Hunter1類是完全一樣的.

下面開始構造新的類來強化hunt()方法.


抽象裝飾角色類   HuntOperation:

無論, 是訓狗trainDog() 還是拍照takePic() 都是對hunt()的增強.

我們新增1個HuntOperation類,  讓它繼承Hunter類,  讓它能重寫hunt()方法.

public abstract class HuntOperation extends Hunter{
	
	private Hunter hunter = null;
	
	//setting component
	public HuntOperation(Hunter hunter){
		this.hunter = hunter;
	}
	
	//actually execute component's hunt() method
	@Override
	public void hunt(){
		if (null != hunter){
			hunter.hunt();
		}
	}
}

注意, 這個類有1個Hunter對象成員.


在hunt()方法裏, 執行的實際上是成員Hunter對象的hunt()方法.


具體的裝飾行爲類 HuntTakePic():

照相類, 就讓它繼承抽象行爲類 HuntOperation類.

public class HuntTakePic extends HuntOperation{
	public HuntTakePic(Hunter hunter) {
		super(hunter);
	}

	@Override
	public void hunt(){
		super.hunt();
		this.takePic();
	}
	
	public void takePic(){
		System.out.println("Taking a Picture!");
	}
}

可見, 它先執行性成員Hunter對象的hunt()方法, 在執行自己的照相方法.


具體的裝飾行爲類 HuntTrainDog():

同樣, 這個類也繼承HuntOperation.

先執行Hunter對象的hunt()方法, 再執行自己的TrainDog方法.

public class HuntTrainDog extends HuntOperation{

	public HuntTrainDog(Hunter hunter) {
		super(hunter);
		// TODO Auto-generated constructor stub
	}
	
	@Override
	public void hunt(){
		super.hunt();
		this.trainDog();
	}
	
	public void trainDog(){
		System.out.println("Training the dog!");
	}

}

客戶端代碼:

客戶端代碼如下:

先構建1個Hunter對象h, 然後再基於h構建HuntTakePic htp,  然後在基於htp構建HuntTrainDog 對象htd.

最後執行htd的hunt()方法.



	        Hunter h = new Hunter();
		HuntTakePic htp = new HuntTakePic(h);
		HuntTrainDog htd = new HuntTrainDog(htp);
		
		htd.hunt();


實際上, 就先執行Hunter對象的hunt(), 再執行HuntTakePic對象的hunt(), 最後才執行HuntTrainDog對象的hunt()

輸出:

Hunter is Hunting!
Taking a Picture!
Training the dog!



UML:




裝飾模式的靈活性:

好了, 這種模式一樣能增強Hunter類的hunt()

那麼它到底比第一種方法靈活在哪裏呢.


很簡單, 假如另1類獵人想調換訓狗, 和照相的順序, 只需修改客戶端的構建循序, 並需要再新增1個子類.

這就是裝飾模式的靈活性.


有人會覺得, 裝飾模式同樣需要增加子類啊,  實際上, 假如我們爲Hunter的hunt()再增加1個子行爲, 例如給獵物剝皮.

那麼只需要增加1個抽象行爲HuntOperation的子類, 而不是Hunter的子類..





五, 小結

其實想深一層, 裝飾模式之所以叫裝飾模式.

是因爲裝飾不能改變原有的功能的行爲.


就如上面的例子,  Hunter裏原來的hunt()方法沒有改變,  只是增加了一些額外的功能.


所以如果需要從根本上改變Hunter裏hunt()的代碼, 裝飾模式就不適用了,  還是建議用增加Hunter子類的方法.





















發佈了288 篇原創文章 · 獲贊 300 · 訪問量 63萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章