-
什麼是裝飾模式
裝飾( Decorator )模式又叫做包裝模式。通過一種對用戶端透明的方式來擴充對象的功能,是繼承關系的一個替換方案。
- 裝飾模式的結構
-
裝飾模式的角色和職責
抽象元件角色: 一個抽象接口,是被裝飾類和裝飾類的父接口。
具體元件角色:為抽象元件的實作類。
抽象裝飾角色:包含一個元件的引用,并定義了與抽象元件一緻的接口。
具體裝飾角色:為抽象裝飾角色的實作類。負責具體的裝飾。
以一個例子來解釋一下,如果我們現在要想造一輛車,對 ! 就是車, Car。
大家都知道,不同的車功能不同,有的車可以跑,廢話!有的車可以飛,有的車可以遊泳,有的車可以跳躍。。。别激動,我就舉個例子。
按照傳統的面向對象的思想,我們肯定先定義一個Car的父類,在這個父類中定義一些所有車都具有的功能,比如run(),然後再派生出各種子類繼承這個Car父類,然後在子類中再實作自己獨特的功能。例如,我聲明一個FlyCar繼承自Car,然後在FlyCar中實作父類的run(),在實作自己獨特的fly()方法。你是不是這麼想的,别不承認,一般人都是醬紫想的。我也是這麼想的,起初我還很自豪,因為我用到了面向對象的思想!!!
但是,如果現在我想造一個既能遊泳又能飛的車怎麼辦?難道我定義一個FlySwimCar繼承自Car,OK,當然可以。但是,随着科技的發展,車的功能越來越豐富,你難道要不停的定義各種組合功能的車嗎,你應該知道排列組合吧~~~這顯然不符合程式員偷懶的個性。
是以,就有了 裝飾模式。我們邊看代碼,邊解釋。
首先,定義一個Car基類,當然,這個必須是接口,你懂的,你不可能直接new 一個Car出來吧,誰知道這個Car是具有哪個功能的Car。但是這個接口Car中應該包含最基本的車的功能。
//Car.java
public interface Car {
public void run();
// 顯示車的功能
public void show();
}
最普通的車不過于實作這個接口了,這個車除了能跑之外,啥都不能幹。對,這就是傳說中的 【跑車】。
//RunCar.java
public class RunCar implements Car{
@Override
public void run() {
System.out.println("run...");
}
@Override
public void show() {
this.run();
}
}
然後,我想造一輛能Fly的車,怎麼辦?
我們先定義一個抽象類CarDecorator,這個類幹嘛的呢?就是對車進行包裝,為啥是抽象的呢?因為你造一個能Fly的車和造一個能Swim的車,用的材料啥的肯定都不同吧,也就是說包裝的内容都不同吧,是以,這個CarDecorator中定義的是包裝類共有操作。就好比,把一輛普通的車改造之前,不論你是改裝成能Fly的車還是能Swim的車,你首先是不是獲得這個普通的車,獲得普通車這個操作就是在CarDecorator基類中定義的。
//CarDecorator.java
public abstract class CarDecorator {
private Car car;
public CarDecorator(Car car) {
this.car = car;
}
//提供方法擷取包裝之前的Car
//為什麼要擷取包裝之前的Car,廢話,不獲得之前的Car,你怎麼添加新的功能?
//那麼添加功能的方法在哪?我怎麼沒瞅見,别急呀,往下看,肯定是特定的裝飾者添加特定的功能呀。
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
//為了顯示車具有的功能,定以了這個show()方法
public abstract void show();
}
好了,現在我們就可以造能飛的車了。
//FlyCarDecorator.java
public class FlyCarDecorator extends CarDecorator {
public FlyCarDecorator(Car car) {
super(car);
}
@Override
//顯示車的功能
public void show() {
//先顯示原始車的功能,這下知道為啥在CarDecorator中獲得改造之前的車了吧。
this.getCar().show();
this.fly();//添加新的功能
}
public void fly(){
System.out.println("fly...");
}
}
同理,我們可以造一個能Swim的車子。
//SwimCarDecorator.java
public class SwimCarDecorator extends CarDecorator{
public SwimCarDecorator(Car car) {
super(car);
}
@Override
public void show() {
this.getCar().show();
this.swim();
}
public void swim(){
System.out.println("swim...");
}
}
醬紫的話,主函數就會變得很清爽。
//MainClass.java
public class MainClass {
public static void main(String[] args) {
Car car = new RunCar();
car.show();
System.out.println("--------------------");
SwimCarDecorator swimCar = new SwimCarDecorator(car);
swimCar.show();
System.out.println("--------------------");
FlyCarDecorator flyCar = new FlyCarDecorator(car);
flyCar.show();
System.out.println("--------------------");
}
}
運作結果:
run...
--------------------
run...
swim...
--------------------
run...
fly...
--------------------
呵呵呵,是不是很清爽。但是你有木有發現一個問題,你說造一個能Fly能Swim的車,現在卻隻造了一個能run和Siwm,能run和Fly的車,還要不要臉了。。。
各位看官,别激動!!!馬上造。馬上造。
正常人的思維是不是醬紫的,我有一個car,隻能run,跑車嘛!!!現在改造(裝飾)一下,能遊泳了,然後我希望既能遊泳也能飛,怎麼辦?廢話,當然是把能遊泳的車加一個翅膀啥的。是不是?嗯,這次你想對了。我也是怎麼想的。
現在我們改我們的main函數:
//MainClass.java
public class MainClass {
public static void main(String[] args) {
Car car = new RunCar();
car.show();
System.out.println("--------------------");
//1
Car swimCar = new SwimCarDecorator(car);
swimCar.show();
System.out.println("--------------------");
//2 3
Car flyCar = new FlyCarDecorator(swimCar);
flyCar.show();
System.out.println("--------------------");
}
}
改了三處,和原來的main函數比較下,看見沒有。為什麼這麼改,你們不都是怎麼想的嗎???忘了?往上看看。
為了适應main函數這麼改,我們修改其他的檔案呢。我們看一下:
原來FlyCarDecorator的構造方法中傳遞的是Car,你現在傳遞的是swimCar,swimCar在沒改之前是啥類型來着,對,是SwimCarDecorator類型。辣麼,你現在知道怎麼改了吧。
我們讓CarDecorator實作Car接口不就完了嗎?
然後分别在FlyDecorator中實作Car中沒有實作的run方法
好了,代碼應該不報錯了吧。。。
現在看下運作結果:
怎麼樣,我沒食言吧,說給你造個能fly能swim的就給你造了。關鍵代碼寫得還是辣麼的優雅!!!嘿嘿嘿,代碼不是我寫的,我隻是分析下。
但是,有人可能會問,你說改這就改這,說改那就改那,到底改的合不合理呢?唉,你别說,這麼一改,比之前還真的合理。
首先,是main方法中的調用邏輯,之前已經說過了,這肯定是符合大多人的思考習慣的。
然後,我們看下CarDecorator實作Car接口合不合理,我們看他的子類FlyDecorator,他繼承自CarDecorator,CarDecorator實作了Car接口,是以,FlyDecorator要實作Car中的接口,Car中有兩個方法一個是show()和run(),show()方法在CarDecorator中已經定義為抽象方法了,已經被FlyDecorator實作了,現在就剩下run()方法沒有實作了,是以,FlyDecorator要實作run()方法,你想想,FlyDecorator實作run方法合理嗎???我覺得非常合理,你給車裝個翅膀讓他飛,他就不能跑了?或許人家跑的更快了呢,或許人家跑的姿勢更優雅了呢?是以你也得給FlyDecorator改造車的同時修改原有功能的機會,是不是?是以我覺得很合理。同理,SwimCarsDecorator中的也要實作Car接口中的run()方法。