裝飾者設計模式


在現實生活中,常常需要對現有產品增加新的功能或美化其外觀,如房子裝修、相片加相框等。在軟件開發過程中,有時想用一些現存的組件。這些組件可能只是完成了一些核心功能。但在不改變其結構的情況下,可以動態地擴展其功能。所有這些都可以釆用裝飾模式來實現。

裝飾模式的定義與特點

裝飾(Decorator)模式的定義:指在不改變現有對象結構的情況下,動態地給該對象增加一些職責(即增加其額外功能)的模式,它屬於對象結構型模式。

  • 采用裝飾模式擴展對象的功能比采用繼承方式更加靈活。
  • 可以設計出多個不同的具體裝飾類,創造出多個不同行為的組合。

其主要缺點是:裝飾模式增加了許多子類,如果過度使用會使程序變得很復雜。

UML類圖

應用實例

星巴克咖啡訂單項目(咖啡館):

  1. 咖啡種類/單品咖啡:Espresso(意大利濃咖啡)、ShortBlack、LongBlack(美式 咖啡)、Decaf(無因咖啡)

  2. 調料:Milk、Soy(豆漿)、Chocolate

  3. 要求在擴展新的咖啡種類時,具有良好的擴展性、改動方便、維護方便

  4. 使用OO的來計算不同種類咖啡的費用: 客戶可以點單品咖啡,也可以單品咖 啡+調料組合


代碼示例

首先抽象出一個Drink類,它是一個抽象構件(Component)角色:定義一個抽象接口以規范准備接收附加責任的對象

public abstract class Drink {

	public String des; // 描述
	private float price = 0.0f;
	// 省略setter and getter方法
	
	//計算費用的抽象方法
	//子類來實現
	public abstract float cost();
	

接下來是具體構件角色,但在這里,我們有EspressoShortBlackLongBlack等,所以我們定義一個Coffee的緩沖層,因為獲取價格的這個方法,下放到具體子類也是一樣的,所有加了一個緩沖層。

public class Coffee  extends Drink {
	@Override
	public float cost() {
		// TODO Auto-generated method stub
		return super.getPrice();
	}	
}

接下來就是一個具體的構件角色了,

public class Espresso extends Coffee {
	
	public Espresso() {
		setDes(" 意大利咖啡 ");
		setPrice(6.0f);
	}
}
public class LongBlack extends Coffee {

	public LongBlack() {
		setDes(" longblack ");
		setPrice(5.0f);
	}
}
public class ShortBlack extends Coffee{
	
	public ShortBlack() {
		setDes(" shortblack ");
		setPrice(4.0f);
	}
}
public class DeCaf extends Coffee {

	public DeCaf() {
		setDes(" 無因咖啡 ");
		setPrice(1.0f);
	}
}

以上是四個具體的構件角色。


現在要往咖啡中加調料:Milk、Soy(豆漿)、Chocolate 等

我們也是通過抽象裝飾(Decorator)角色:繼承抽象構件,並包含具體構件的實例,可以通過其子類擴展具體構件的功能。

public class Decorator extends Drink {
	private Drink obj;
	
	public Decorator(Drink obj) { //組合
		// TODO Auto-generated constructor stub
		this.obj = obj;
	}
	
	@Override
	public float cost() {
		// TODO Auto-generated method stub
		// getPrice 自己價格
		return super.getPrice() + obj.cost();
	}
	
	@Override
	public String getDes() {
		// TODO Auto-generated method stub
		// obj.getDes() 輸出被裝飾者的信息
		return des + " " + getPrice() + " && " + obj.getDes();
	}
}

抽象裝飾中聚合了Drink obj,那后續在使用中,我們通過具體傳入EspressoShortBlack等來裝配。


具體裝飾

public class Milk extends Decorator {

	public Milk(Drink obj) {
		super(obj);
		// TODO Auto-generated constructor stub
		setDes(" 牛奶 ");
		setPrice(2.0f); 
	}

}
public class Soy extends Decorator{

	public Soy(Drink obj) {
		super(obj);
		// TODO Auto-generated constructor stub
		setDes(" 豆漿  ");
		setPrice(1.5f);
	}

}
//具體的Decorator, 這里就是調味品
public class Chocolate extends Decorator {

	public Chocolate(Drink obj) {
		super(obj);
		setDes(" 巧克力 ");
		setPrice(3.0f); // 調味品 的價格
	}

}

使用測試

public class CoffeeBar {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		// 裝飾者模式下的訂單:2份巧克力+一份牛奶的LongBlack

		// 1. 點一份 LongBlack
		Drink order = new LongBlack();
		System.out.println("費用1=" + order.cost());
		System.out.println("描述=" + order.getDes());

		// 2. order 加入一份牛奶
		order = new Milk(order);

		System.out.println("order 加入一份牛奶 費用 =" + order.cost());
		System.out.println("order 加入一份牛奶 描述 = " + order.getDes());

		// 3. order 加入一份巧克力

		order = new Chocolate(order);

		System.out.println("order 加入一份牛奶 加入一份巧克力  費用 =" + order.cost());
		System.out.println("order 加入一份牛奶 加入一份巧克力 描述 = " + order.getDes());

		// 3. order 加入一份巧克力

		order = new Chocolate(order);

		System.out.println("order 加入一份牛奶 加入2份巧克力   費用 =" + order.cost());
		System.out.println("order 加入一份牛奶 加入2份巧克力 描述 = " + order.getDes());
	
		System.out.println("===========================");
		
		Drink order2 = new DeCaf();
		
		System.out.println("order2 無因咖啡  費用 =" + order2.cost());
		System.out.println("order2 無因咖啡 描述 = " + order2.getDes());
		
		order2 = new Milk(order2);
		
		System.out.println("order2 無因咖啡 加入一份牛奶  費用 =" + order2.cost());
		System.out.println("order2 無因咖啡 加入一份牛奶 描述 = " + order2.getDes());

	
	}

}

測試結果:

費用1=5.0
描述= longblack 
order 加入一份牛奶 費用 =7.0
order 加入一份牛奶 描述 =  牛奶  2.0 &&  longblack 
order 加入一份牛奶 加入一份巧克力  費用 =10.0
order 加入一份牛奶 加入一份巧克力 描述 =  巧克力  3.0 &&  牛奶  2.0 &&  longblack 
order 加入一份牛奶 加入2份巧克力   費用 =13.0
order 加入一份牛奶 加入2份巧克力 描述 =  巧克力  3.0 &&  巧克力  3.0 &&  牛奶  2.0 &&  longblack 
===========================
order2 無因咖啡  費用 =1.0
order2 無因咖啡 描述 =  無因咖啡 
order2 無因咖啡 加入一份牛奶  費用 =3.0
order2 無因咖啡 加入一份牛奶 描述 =  牛奶  2.0 &&  無因咖啡 

以上就是裝飾者設計模式的簡單應用了,JDK源碼中的IO流就充分使用了裝飾者設計模式。

IO流中的裝飾者設計模式

以InputStream為例,


InputStream就是抽象構件(Component)角色

FilterInputStream就是抽象裝飾角色(Decorator),繼承並持有一個InputStram的引用

剩下兩個角色自己對應,


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM