LuoguP4463 [集訓隊互測2012] calc DP+拉格朗日插值
目錄
基本介紹
裝飾器模式意圖為一個物件擴充套件新的功能,且不改變原有的結構,裝飾器模式屬於結構型設計模式
一般的,我們為了擴充套件一個類經常使用繼承方式實現,由於繼承為類引入靜態特徵,並且隨著擴充套件功能的增多,子類會很膨脹
使用場景
- 擴充套件一個類的功能
- 動態增加功能,動態撤銷
假設有一家咖啡公司,姑且咱就叫怪獸咖啡吧,該咖啡公司是以擴充套件速度最快而聞名的咖啡公司(像瑞幸咖啡一樣),但是最近由於擴充套件速度太快,它們想重新設計一套訂單系統,以滿足它們現在的飲料需求。
它們最初始的設計如下:
然鵝使用者在購買咖啡時,通常會新增一些調料,例如:摩卡、豆漿、奶泡等等。怪獸咖啡並根據業務去計算相應的總價,這就要求咖啡公司在設計訂單時需要考慮這些調料的部分。然後就設計出這麼一套龐雜的系統,因為奶茶種類太多,調料太多。種類*調料 = 類爆炸,實在太瘋狂了
這麼設計的問題顯而易見
- 調料價格變化時,需要更改現有程式碼
- 出現新的調料,需要新增新的方法,並改動超類中的
cost()
方法 - 以後開發新的飲料,對於這些飲料,某些調料並不適合,但是這個設計中,子類仍需繼承那些不需要的方法
裝飾器模式
從上述的設計方案來看,這顯然並不是一個聰明的結果,因為會遇到 類爆炸、設計過於冗餘,以及基類加入的新功能並不一定適用於所有子類
所以我們要考慮換一種方式,設想一下 我們能不能 以飲料為紅花,用調料作為 綠葉 裝飾它
比如,客戶想要 摩卡和奶泡 搭配黑莓咖啡(口味獨特),那麼,我們怎麼做呢?
- 拿黑莓咖啡(DarkRoast)作為物件
- 用摩卡(Mocha)物件裝飾它
- 用奶泡(Whip)物件裝飾它
- 呼叫 cost() 方法,並通過依賴將調料價格和摩卡價格想加
上圖詳細的介紹了,裝飾模式的總體過程
上圖為裝飾器模式的結構類圖以及各個類的作用說明
好,下面我們詳細介紹訂單系統的正確設計方案
1、我們先設計飲料元件
/** * 建立一個飲料抽象類 */ public abstract class Beverage { String description = "UnKnown Beverage"; public String getDescription() { return description; } public abstract Double cost(); }
2、假設目前店鋪中,有 綜合咖啡、濃縮咖啡、黑莓
public class HouseBlend extends Beverage {
public HouseBlend() {
description = "綜合咖啡";
}
@Override
public Double cost() {
return 23.2;
}
}
public class Expresso extends Beverage{
public Expresso(){
description = "濃縮咖啡";
}
@Override
public Double cost() {
return 19.9;
}
}
public class DarkRoast extends Beverage {
public DarkRoast() {
description = "黑莓咖啡";
}
@Override
public Double cost() {
return 21.8;
}
}
3、下面我們實現調料類的抽象類,也就是裝飾者類
/**
* 調料抽象類
*/
public abstract class Condiment extends Beverage {
Beverage beverage;
Condiment(Beverage decoratedBeverage){
this.beverage = decoratedBeverage;
}
public abstract String getDescription();
}
4、 然後我們再實現相應的 Mocha、Whip、Soy 的調料程式碼
/**
* 摩卡
*/
public class Mocha extends Condiment {
public Mocha(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + "【摩卡】";
}
@Override
public Double cost() {
return new BigDecimal(beverage.cost().toString()).add(new BigDecimal(2.2)).doubleValue();
}
}
/**
* 豆漿
*/
public class Soy extends Condiment {
public Soy(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription(){
return beverage.getDescription() + "【豆漿】";
}
@Override
public Double cost() {
return new BigDecimal(beverage.cost().toString()).add(new BigDecimal(3.6)).doubleValue();
}
}
/**
* 奶泡
*/
public class Whip extends Condiment {
public Whip(Beverage beverage){
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + "【奶泡】";
}
@Override
public Double cost() {
return new BigDecimal(beverage.cost().toString()).add(new BigDecimal(2.7)).doubleValue();
}
}
好了,參考裝飾者模式,訂單模組的基本實現已經基本完成。下面是測試程式碼
public class Client {
public static void main(String[] args) {
//一杯DarkRoast 不需要調料
Beverage darkRoast = new DarkRoast();
System.out.println(darkRoast.getDescription() + "," + darkRoast.cost());
//再點一個濃縮咖啡 加 雙倍摩卡 一份奶泡
Beverage beverage = new Expresso();
beverage = new Mocha(beverage);
beverage = new Mocha(beverage);
beverage = new Whip(beverage);
System.out.println(beverage.getDescription() + "," + beverage.cost());
}
}
IO體系中的裝飾器
由圖可見,InputStream就是裝飾者模式中的超類(Component),
ByteArrayInputStream,FileInputStream相當於 被裝飾者(ConcreteComponent),這些類都提供了最基本的位元組讀取功能。
而另外一個和這兩個類是同一級的類FilterInputStream 即為 抽象裝飾者(AbstarctDecorator)
BufferedInputStream,DataInputStream,PushbackInputStream(都繼承了FilterInputStream類),它們為 裝飾者(Decorator),在原有基礎功能上都實現了功能的擴充套件和增強。
例:用BufferedInputStream 裝飾 FileInputStream,和上面Mocha(摩卡) 裝飾 DarkRoast(黑莓)如出一轍
File file = new File ("hello.txt");
FileInputStream in=new FileInputStream(file);
BufferedInputStream inBuffered=new BufferedInputStream (in);