1. 程式人生 > >裝飾模式(Decorate Pattern)

裝飾模式(Decorate Pattern)

裝飾模式定義:裝飾模式動態的將責任附加到物件上,若要擴充套件功能,裝飾模式提供了比繼承更有彈性的替代方案

看下下面的例子,總共有兩種咖啡:Decaf、Espresso,另有兩種調味品:Mocha、Whip(3種設計的主要差別在於抽象方式不同)

設計一:

即使新增再多的調味品,咖啡依然是咖啡,在抽象的過程中並沒有考慮咖啡和調味品之間的關係

當咖啡和調味品的種類很多時,將會產生大量的類,如果一種咖啡的價格發生變動,需要找到所有相關的類逐一修改

設計二:

將調味品作為Coffee類的屬性,比起設計一,類的數量大大減少,相應的,程式結構也更加清晰

public class Coffee {
	private boolean mocha;
	private boolean whip;
	
	public double cost(){
		double price = 0d;
		if(mocha){
			price += 0.5;
		}
		if(whip){
			price += 0.1;
		}
		return price;
	}
	
	public void addMocha(){
		this.mocha = true;
	}
	
	public void addWhip(){
		this.whip = true;
	}
}
public class Decaf extends Coffee{
	public double cost(){
		double price = super.cost();
		price += 2.0;
		return price;
	}
}
public class Espresso extends Coffee {
	public double cost(){
		double price = super.cost();
		price += 2.5;
		return price;
	}
}

測試一下:

public class Test {
	public static void main(String[] args) {
		Coffee coffee = new Decaf();
		coffee.addMocha();
		coffee.addWhip();
		//2.6
		System.out.println(coffee.cost());
	}
}

考慮到下面幾個問題,設計二有明顯的不足:

1,如果調味品的種類較多,Coffee類將會變得相當龐大,難以維護

2,類本身不夠靈活,無法處理顧客希望新增雙倍的Mocha的場景

3,新增一種新的咖啡IceCoffee,如果IceCoffee不能加Mocha,由於IceCoffee類繼承自Coffee類,IceCoffee類依然從父類繼承了addMocha()方法,這就需要在IceCoffee類中重寫一個空的addMocha()方法

設計三:裝飾模式

裝飾模式分為3個部分:

1,抽象元件Component:對應Coffee類

2,具體元件ConcreteComponent:對應具體的咖啡,如:Decaf,Espresso

3,裝飾者Decorator:對應調味品,如:Mocha,Whip

裝飾模式有3個特點:

1,具體元件和裝飾者都繼承自抽象元件(Decaf、Espresson、Mocha和Whip都繼承自Coffee),並且裝飾者持有抽象元件的引用

2,可以使用裝飾者組合具體元件創造出新的類(Mocha組合Decaf創造出MochaDecaf)

3,過程2可以重複,直到創造出需要的類

使用裝飾模式,想要獲得一個WhipDoubleMochaEspresso是很容易的:


public interface Coffee {
	public double cost();
}
public class Espresso implements Coffee {
	public double cost(){
		return 2.5;
	}
}
public class Decorator implements Coffee {
	private Coffee coffee;
	
	public Decorator(Coffee coffee){
		this.coffee = coffee;
	}
	
	public double cost(){
		return coffee.cost();
	}
}
public class Whip extends Decorator {
	public Whip(Coffee coffee){
		super(coffee);
	}
	
	public double cost(){
		return super.cost() + 0.1;
	}
}
public class Mocha extends Decorator {
	public Mocha(Coffee coffee){
		super(coffee);
	}
	
	public double cost(){
		return super.cost() + 0.5;
	}
}

測試一下:

public class Test {
	public static void main(String[] args) {
		Coffee coffee = new Mocha(new Mocha(new Whip(new Espresso)));
		//3.6(0.5 + 0.5 + 0.1 + 2.5)
		System.out.println(coffee.cost());
	}
}

看到這裡是不是想到了Java中很常用的InputStream和OutputStream類?沒錯,它們都是使用裝飾模式設計的

當然設計的時候可以靈活一些,如果只有固定一種Coffee,可以設計成下面的結構


如果只有一種調味品,下面的設計方式也是可以的


裝飾模式的缺點:

1,裝飾模式雖然擴充套件性較高,但是沒有設計二簡潔,類的數量略多(比設計一少很多),如何取捨可擴充套件性和簡潔性是個問題。如果需求比較明確,並且後期發生變化的概率不大,沒必要直接使用裝飾模式,設計二更快速高效

2,很難搞清楚一個類究竟被裝飾了多少層,可能是1層,也可能是100層

3,在某些場景下,可能需要按照一定的順序進行裝飾,稍不注意,就會產生異常

裝飾模式與建造者模式之間的區別:

裝飾模式的構造過程是不穩定的(是否需要裝飾,裝飾多少層都是可以自由調整的),建造者模式的建造過程是穩定的