裝飾器模式
裝飾器模式(Decorator Pattern)允許向一個現(xiàn)有的對象添加新的功能荡碾,同時又不改變其結構矾睦。這種類型的設計模式屬于結構型模式勾栗,它是作為現(xiàn)有類的一個包裝苍苞。裝飾器在代碼程序中適用于以下場景:
- 用于擴展 一個類的功能或者給一個類添加附加職責
- 動態(tài)的給一個對象添加功能狰晚,這些功能可以再動態(tài)的撤銷
為什么要用裝飾器模式
裝飾器模式能很好的解決過多的繼承所帶來的問題筒饰,下面我們通過一個例子來看看裝飾器模式的作用。生活中我們很多人都很喜歡喝奶茶壁晒,只喝奶茶又感覺有點太單調瓷们,會加一些配料,比如珍珠秒咐、脆啵啵谬晕、芋圓等,下面我們通過代碼來模擬一下携取。
最原始不放任何調料的特調奶茶
/**
* @author: Winston
* @createTime: 2021/7/1
*/
public class MilkTea {
public String info() {
return "特調奶茶";
}
public double price() {
return 8D;
}
}
由于我想吃點珍珠攒钳,于是我通過繼承的方式給奶茶增加珍珠
public class MilkTearWithPearl extends MilkTea {
@Override
public String info() {
return super.info() + "加珍珠";
}
@Override
public double price() {
// 奶茶加上珍珠的價格
return super.price() + 2D;
}
}
我還想要一杯珍珠加上脆啵啵的奶茶
public class MilkTearWithPearlAndBoo extends MilkTea {
@Override
public String info() {
return super.info() + "加珍珠加脆啵啵";
}
@Override
public double price() {
// 加珍珠加脆啵啵后的價格
return super.price() + 2D + 3D;
}
}
我同事想要一杯加珍珠加脆啵啵加西米露的奶茶
public class MilkTearWithPearlAndBooAndSago extends MilkTea {
@Override
public String info() {
return super.info() + "加珍珠加脆啵啵加西米露";
}
@Override
public double price() {
// 加珍珠加脆啵啵加西米露后的價格
return super.price() + 2D + 3D + 2D;
}
}
測試類
public class MilkTeaTest {
public static void main(String[] args) {
MilkTea milkTea = new MilkTea();
System.out.println(milkTea.info() + ",總價格:"+milkTea.price());
System.out.println("====================================================");
MilkTearWithPearl milkTearWithPearl = new MilkTearWithPearl();
System.out.println(milkTearWithPearl.info() + "雷滋,總價格:"+milkTearWithPearl.price());
System.out.println("====================================================");
MilkTearWithPearlAndBoo milkTearWithPearlAndBoo = new MilkTearWithPearlAndBoo();
System.out.println(milkTearWithPearlAndBoo.info() + "不撑,總價格:"+milkTearWithPearlAndBoo.price());
System.out.println("====================================================");
MilkTearWithPearlAndBooAndSago milkTearWithPearlAndBooAndSago = new MilkTearWithPearlAndBooAndSago();
System.out.println(milkTearWithPearlAndBooAndSago.info() + ",總價格:"+milkTearWithPearlAndBooAndSago.price());
System.out.println("====================================================");
}
}
特調奶茶晤斩,總價格:8.0
====================================================
特調奶茶加珍珠焕檬,總價格:10.0
====================================================
特調奶茶加珍珠加脆啵啵,總價格:13.0
====================================================
特調奶茶加珍珠加脆啵啵加西米露澳泵,總價格:15.0
====================================================
從上面的例子我們可以看出只要加的小料不同那么我就要新創(chuàng)建一個類來繼承奶茶類MikTea实愚,如果只有三種料珍珠、芋圓、脆啵啵不重復添加爆侣,根據(jù)排列組合就有6種方式萍程,更何況有可能有的人會要雙份珍珠,這么一來我們的類就特別特別多兔仰,非常的冗余茫负。有沒有什么方式能夠進行改造呢?下面我們就用裝飾器模式將上面的案例進行改造乎赴,讓大家體會一下用了它有何不同忍法。
裝飾器模式改造案例
首先先創(chuàng)造一個奶茶的抽象類MilkTea,所有的奶茶都必須有介紹信息和價格信息
public abstract class MilkTea {
/**
* 奶茶中的信息
*/
protected abstract String info();
/**
* 奶茶總價格,這個方法需要在具體實現(xiàn)類中實現(xiàn)
*
* @return
*/
protected abstract double price();
}
創(chuàng)造一個基礎款奶茶榕吼,要繼承基類MilkTea
public class BaseMilkTea extends MilkTea{
private String info = "本店招牌特調奶茶";
@Override
protected String info() {
return this.info;
}
@Override
protected double price() {
return 12D;
}
}
再創(chuàng)建一個巧克力奶茶類饿序,同樣要繼承基類MilkTea
public class ChocolateMilkTea extends MilkTea {
private String info = "巧克力奶茶";
@Override
protected String info() {
return this.info;
}
@Override
protected double price() {
return 15D;
}
}
創(chuàng)建一個奶茶的裝飾類MilkTeaDecorate,同樣這個類也繼承MilkTea羹蚣,這里大家可能會有疑問原探,既然MilkTeaDecorate這個抽象類的方法和MilkTea抽象類的方法一樣為什么還要單獨寫一個類。這里的話是為了能夠區(qū)分出哪個是裝飾者顽素,哪個是被裝飾者咽弦。
public abstract class MilkTeaDecorate extends MilkTea {
/**
* 要添加小料的奶茶,通過構造函數(shù)傳入奶茶信息
*/
public MilkTea milkTea;
public MilkTeaDecorate(MilkTea milkTea) {
this.milkTea = milkTea;
}
/**
* 所有的調料裝飾者都必須重新實現(xiàn)info()方法
* 這樣才能夠得到所選奶茶的整體描述
*
* @return
*/
@Override
protected String info() {
return this.info();
}
/**
* 所有的調料裝飾者都必須重新實現(xiàn)price()方法
* 這樣才能夠得到所選奶茶的整體價格
*
* @return
*/
@Override
protected double price() {
return this.price();
}
}
編寫珍珠裝飾類
public class PearlDecorate extends MilkTeaDecorate {
public PearlDecorate(MilkTea milkTea) {
super(milkTea);
}
@Override
protected double price() {
return this.milkTea.price() + 2D;
}
@Override
protected String info() {
return this.milkTea.info() + "加一份珍珠";
}
}
脆啵啵裝飾類
public class BooDecorate extends MilkTeaDecorate {
public BooDecorate(MilkTea milkTea) {
super(milkTea);
}
@Override
protected double price() {
return this.milkTea.price() + 3D;
}
@Override
protected String info() {
return this.milkTea.info() + "加一份波波";
}
}
西米露裝飾類
public class SagoDecorate extends MilkTeaDecorate {
public SagoDecorate(MilkTea milkTea) {
super(milkTea);
}
@Override
protected double price() {
return this.milkTea.price() + 3D;
}
@Override
protected String info() {
return this.milkTea.info() + "加一份西米露";
}
}
測試類:
public class MilkTeaTest {
public static void main(String[] args) {
//1.一份原始的特調奶茶
System.out.println("=======================================");
MilkTea milkTea;
milkTea = new BaseMilkTea();
System.out.println(milkTea.info() + "總價價格:" + milkTea.price());
// 想要加一份珍珠
milkTea = new PearlDecorate(milkTea);
System.out.println(milkTea.info() + "總價價格:" + milkTea.price());
// 加一份脆啵啵
milkTea = new BooDecorate(milkTea);
System.out.println(milkTea.info() + "總價價格:" + milkTea.price());
//1.一份巧克力奶茶
System.out.println("=======================================");
MilkTea milkTea2;
milkTea2 = new ChocolateMilkTea();
System.out.println(milkTea2.info() + "總價價格:" + milkTea2.price());
// 想要加一份珍珠
milkTea2 = new PearlDecorate(milkTea2);
System.out.println(milkTea2.info() + "總價價格:" + milkTea2.price());
// 加一份脆啵啵
milkTea2 = new BooDecorate(milkTea2);
System.out.println(milkTea2.info() + "總價價格:" + milkTea2.price());
// 再加一份珍珠
milkTea2 = new PearlDecorate(milkTea2);
System.out.println(milkTea2.info() + "總價價格:" + milkTea2.price());
System.out.println("=======================================");
}
}
結果:
=======================================
本店招牌特調奶茶總價價格:12.0
本店招牌特調奶茶加一份珍珠總價價格:14.0
本店招牌特調奶茶加一份珍珠加一份波波總價價格:17.0
=======================================
巧克力奶茶總價價格:15.0
巧克力奶茶加一份珍珠總價價格:17.0
巧克力奶茶加一份珍珠加一份波波總價價格:20.0
巧克力奶茶加一份珍珠加一份波波加一份珍珠總價價格:22.0
=======================================
案例小結
通過上面這個案例胁出,我們可以看出裝飾者(裝飾器)模式的特點型型。
- 裝飾者類擁有被裝飾者類的對象,一般是當做構造函數(shù)傳入的全蝶。
- 在裝飾者類當中調用被裝飾者類的方法闹蒜,封裝成新的功能方法。
- 裝飾者模式主要是利用多態(tài)抑淫,將子類對象作為參數(shù)互相傳遞(主要是為了傳遞實現(xiàn)的函數(shù))绷落,達到互相裝飾的效果,從而減少代碼重復率丈冬,優(yōu)化代碼結構嘱函。
從上面的例子我們又可以看出跟靜態(tài)代理有所相似,靜態(tài)代理的被代理類和代理類都實現(xiàn)了同一接口埂蕊,都是對類功能進行加強往弓。這邊裝飾者模式和靜態(tài)代理有何區(qū)別的?裝飾者模式和靜態(tài)代理的最大區(qū)別就是職責不同蓄氧。代理模式(Proxy Pattern)函似,為其它對象提供一種代理以控制對這個對象的訪問。裝飾模式(Decorator Pattern)喉童,動態(tài)地給一個對象添加一些額外的職責撇寞。換句話說:代理模式的目標是控制對被代理對象的訪問,而裝飾模式是給原對象增加額外功能。雖然代理模式也可以實現(xiàn)對被代理對象功能的增強蔑担,但其核心是隱藏對被代理類的訪問牌废。