設計模式05----裝飾者模式
1. 什麼時候使用裝飾者模式
比如有一家店賣飲品,飲品就有不少種,每一種還可以加項,比如給可樂加冰,加糖,兌水什麼的,每次加項的價格還不同,就會將程式碼弄的很繁瑣,這種情況下就可以使用裝飾者模式來實現.
2. 什麼是裝飾者模式
上述的例子中,可以以飲品為主體,用不用的各種需求來裝飾它,比如有一個可樂物件,那我用一個加冰物件裝飾一下,再用加糖物件裝飾一下,最後能得到一個加冰加糖可樂,這時候就將原可樂物件擴充套件,得到了加冰和加糖兩種裝飾.
裝飾者模式: 動態地將責任附加到物件上,對擴充套件功能來說,裝飾者比繼承更有彈性更靈活(因為子類繼承父類擴充套件功能的前提,是已知要擴充套件的功能是什麼樣的,而這是在編譯時就要確定的,但是裝飾者模式可以實現動態(在執行時)去擴充套件功能).
3. 裝飾者模式結構
Decorator:是裝飾者的父類,每個裝飾者都需要繼承這個抽象類(或實現這個介面).
ConcreteDecoratorA/B:具體的裝飾者,就對應上述例子中的加冰,加糖等.
ConcreteComponent:具體的物件,就是上述例子中的可樂.
Component:裝飾者模式中最頂級的父類,裝飾者與被裝飾者都是它的子類或實現類才行.
4. 舉例說明
(1)建立最頂級的父類
飲品類:所有的被裝飾的類都需要繼承它.
package componet;
/**
* Created by zyf on 2017/3/30.
* 裝飾者模式中最頂級的父類
*/
public abstract class 飲品 {
String name;
/**
* 每個飲品的價格不同,所以講price方法抽象化<br/>
* 讓每個實現"飲品"類的子類自己決定是多少錢
* */
public abstract int price();
/***
* 得到飲品的名字
* @return 名字
*/
public String getName(){
return name;
}
}
(2)被裝飾的可樂Component類
package componet;
/**
* Created by zyf on 2017/3/30.
*/
public class 可樂Component extends 飲品 {
public 可樂Component() {
//設定name為可樂
//這個name屬性是從飲品類中繼承來的
name = "可樂";
}
/***
* 實現父類的抽象方法
* @return 可樂的價格
*/
@Override
public int price() { //可樂30塊一瓶~ return 30; } }
(3)被裝飾的啤酒Component類
package componet;
/**
* Created by zyf on 2017/3/30.
*/
public class 啤酒Component extends 飲品{
public 啤酒Component() {
//設定name為啤酒
//這個name屬性是從飲品類中繼承來的
name = "啤酒";
}
/***
* 實現父類的抽象方法
* @return 啤酒的價格
*/
@Override
public int price() { //啤酒3塊一瓶~ return 3; } }
(4)Decorator類,所有裝飾類的父類
package decorator;
import componet.飲品;
/**
* Created by zyf on 2017/3/30.
* 裝飾者模式中,所有裝飾者的父類
*/
public abstract class Decorator extends 飲品 {
/***
* 宣告一個飲品引用,準備接受一個飲品物件<br/>
*/
protected 飲品 yp;
public Decorator(飲品 yp) {
this.yp = yp; } }
(5)裝飾類:加醋Decorator類
package decorator;
import componet.飲品;
/**
* Created by zyf on 2017/3/30.
*/
public class 加醋Decorator extends Decorator {
public 加醋Decorator(飲品 yp) {
super(yp);
}
public void addVinegar(){ System.out.println("還要加醋,加完了"); } /*** * 那麼加醋後的價格應該是多少呢?<br/> * 應該是加粗的價格加飲品的價格 * @return 加醋五塊 */ @Override public int price() { return 5 + yp.price(); } /*** * 再複寫一個名字的方法<br/> * 現在已經不是單純的飲品了 * @return */ @Override public String getName() { //在這裡加個醋 addVinegar(); return "加醋的" + yp.getName(); } }
(6)裝飾類:兌水Decorator類
package decorator;
import componet.飲品;
/**
* Created by zyf on 2017/3/30.
*/
public class 兌水Decorator extends Decorator {
public 兌水Decorator(飲品 yp) {
super(yp);
}
public void 兌水(){ System.out.println("飲料兌水....尷尬不老鐵..."); } /*** * 那麼兌水後的價格應該是多少呢?<br/> * 應該是兌水的價格加飲品的價格 * @return 兌水2塊 */ @Override public int price() { return 2 + yp.price(); } /*** * 再複寫一個名字的方法<br/> * 現在已經不是單純的飲品了 * @return */ @Override public String getName() { 兌水(); return "兌水了的" + yp.getName(); } }
(7)測試類
public static void main(String[] args) {
//可以看到,我們操作的引用一直是這個yp
//但是這個引用指向的物件已經換了好幾次了
//這就是為什麼裝飾類也要是飲品類的子類,因為只有這樣,裝飾類與被裝飾類才能被當做同一個型別使用(通過介面或繼承實現)
飲品 yp = new 可樂Component();
yp = new 兌水Decorator(yp);
yp = new 加醋Decorator(yp);
// 上面與下面這一行是一樣的,是不是和IO流很像?
// yp = new 加醋Decorator(new 兌水Decorator(new 可樂Component()));
System.out.println("飲品名:" + yp.getName() + "---價格:" + yp.price()); }
note:我的理解是:
實際上裝飾者就先拿到被裝飾者的物件(比如飲品--可樂),這個物件可能有自己的一些方法可以被呼叫(比如getPrice);
我們建立裝飾者的物件,然後就可以給被裝飾者進行裝飾,比如重寫被裝飾者物件的方法(getPrice),因為頂級父類是一樣的(因為可樂和Decortor都繼承自飲品這個頂級父類),因此可以重寫。
那對映到IO流中,比如FileInputStream作為被裝飾類,而BufferedInputStream作為具體的裝飾類(它繼承自FIlterInputStream類),同時FileInputStream和FilterInputStream都繼承自InputStream這個頂級父類。因此BufferedInputStream可以裝飾FileInputStream類,比如重寫了InputStream頂級父類的read方法,還添加了一些裝飾方法,比如mark和reset方法。
參考文獻:https://blog.csdn.net/android_zyf/article/details/68343953