Java設(shè)計模式之裝飾器模式
本文僅是個人觀點县爬,如有錯誤請指正
簡介
裝飾器模式(Decorator Pattern)允許向一個現(xiàn)有的對象添加新的功能,同時又不改變其結(jié)構(gòu)枣宫。這種類型的設(shè)計模式屬于結(jié)構(gòu)型模式胯盯,它是作為現(xiàn)有的類的一個包裝。
這種模式創(chuàng)建了一個裝飾類哎甲,用來包裝原有的類,并在保持類方法簽名完整性的前提下饲嗽,提供了額外的功能炭玫。
我們通過下面的實例來演示裝飾器模式的用法。其中貌虾,我們將把一個形狀裝飾上不同的顏色吞加,同時又不改變形狀類。
代碼實現(xiàn)
最近小王很煩惱尽狠,因為公司來了新的業(yè)務(wù)交給他衔憨,客戶是一個咖啡館,需要為他們設(shè)計一套點餐系統(tǒng)袄膏,他們具有商品咖啡践图,飲品,還有配料哩陕,這些各種組合起來就是各種咖啡品類平项,這些各種各樣的組合下來小王一支找不到頭緒,思前想后還是找到了公司里的大牛老李(咋又是老李)悍及,老李一聽需求闽瓢,脫口而出,這不就是裝飾器模式么心赶,小王聽了一愣扣讼,啊,那應(yīng)該怎么設(shè)計呢缨叫,老李說椭符,咱們設(shè)計一抽象類里面放入單品的名字荔燎、計數(shù)、總價計算销钝,然后分別設(shè)計咖啡基類有咨、果汁類、調(diào)料類蒸健,然后各種咖啡果汁基于這些類進行擴展座享。。似忧。渣叛。,小王聽著云里霧里盯捌,老李一看說這個簡單淳衙,我來說你來寫代碼。
-
首先我們設(shè)計咖啡館的抽象類饺著,我們給他加入品名箫攀,單價,計數(shù)變量然后設(shè)置get和set方法瓶籽,再設(shè)計一個話費的抽象方法匠童。
public abstract class BaseDrink { public String description; public Integer amount = 1; private float price = 0f; public void setDescription(String description) { this.description = description; } public String getDescription() { return description + "-" + this.getPrice(); } public float getPrice() { return price; } public void setPrice(float price) { this.price = price * amount; } public void setAmount(Integer amount){ if (amount >= 1){ this.amount = amount; } } /** * 總價 * @return */ public abstract float cost(); }
-
咖啡類讓它繼承咖啡館的抽象類
public class Coffee extends BaseDrink { @Override public float cost() { return super.getPrice(); } }
-
同樣再加入果汁類
public class Juice extends BaseDrink { @Override public float cost() { return super.getPrice(); } }
-
接著就是需要加入的調(diào)料類(裝飾者),這里面我們加入他的構(gòu)造函數(shù)塑顺,讓它把需要加調(diào)料的飲品拿過來,加入調(diào)料再分別計算價格俏险。
public class Decorator extends BaseDrink { private BaseDrink obj; public Decorator(BaseDrink obj) { this.obj = obj; } @Override public float cost() { return super.getPrice() + obj.cost(); } @Override public String getDescription() { return super.description + "-" + super.getPrice() + "&&" + obj.getDescription(); } }
-
接著我們就可以基于上面的類來設(shè)計一杯低咖,設(shè)置好單價
public class Decaf extends Coffee { public Decaf(Integer amount) { super.setDescription("Decaf"); super.setAmount(amount); super.setPrice(3.0f); } }
-
再來點熱巧克力严拒,設(shè)置好單價
public class Chocolate extends Decorator { public Chocolate(BaseDrink obj) { super(obj); super.setDescription("Chocolate"); super.setPrice(3.0f); } }
-
再來點牛奶,設(shè)置單價
public class Milk extends Decorator { public Milk(BaseDrink obj) { super(obj); super.setDescription("Milk"); super.setPrice(2.0f); } }
-
好了 咱們來測試一下效果
public class CoffeeBar { public static void main(String[] args) { BaseDrink order; System.out.println("****************"); order = new Decaf(1); order = new Milk(order); order = new Chocolate(order); order = new Chocolate(order); System.out.println("order1 desc:" + order.getDescription()); System.out.println("order1 price:" + order.cost()); System.out.println("****************"); } }
-
輸出結(jié)果
**************** order1 desc:Chocolate-3.0&&Chocolate-3.0&&Milk-2.0&&Decaf-3.0 order1 price:11.0 ****************
看到結(jié)果竖独,小王一臉崇拜的看著老李裤唠,老李腦袋在燈光下冉冉發(fā)光,形象無比的高大莹痢。种蘸。。竞膳。
擴展
其實java內(nèi)置的IO對象里面也使用到了裝飾器模式航瞭,下面代碼就是我們用來測試的
- 我們新建一個UpperCaseInputStream類讓它繼承FilterInputStream并重寫其中的read方法
public class UpperCaseInputStream extends FilterInputStream {
protected UpperCaseInputStream(InputStream in) {
super(in);
}
@Override
public int read() throws IOException {
int c = super.read();
return c == -1 ? c : Character.toUpperCase((char) (c));
}
@Override
public int read(byte[] b, int offset, int len) throws IOException {
int result = super.read(b, offset, len);
for (int i = 0; i < result; i++) {
b[i] = (byte) Character.toUpperCase((char) (b[i]));
}
return result;
}
}
- 可以看看FilterInputStream源碼繼承了InputStream
/* @author Jonathan Payne
* @since JDK1.0
*/
public
class FilterInputStream extends InputStream {....}
-
測試方法
public class InputTest { public static void main(String[] args) { int c; try { InputStream in = new UpperCaseInputStream(new BufferedInputStream(new FileInputStream("F:\\test.txt"))); while ((c = in.read()) >= 0) { System.out.print((char) c); } } catch (IOException e) { e.printStackTrace(); } } }
總結(jié)
一般的情況下我們?yōu)榱藬U展一個子類,經(jīng)常使用裝飾者模式實現(xiàn)坦辟,由于繼承為類引入靜態(tài)特征刊侯,并且隨著擴展功能的增多,子類會很膨脹所以在不想增加很多子類的情況下我們可以考慮使用裝飾器模式锉走,其中裝飾類和被裝飾類可以獨立發(fā)展滨彻,不會互相耦合藕届,裝飾模式是繼承的一個替代模式,裝飾模式可以動態(tài)擴展一個實現(xiàn)類的功能亭饵。
鏈接
本文代碼倉庫地址:https://gitee.com/singlekingdom/JavaDesignPatterns.git