咱們都知道,可使用兩種方式給一個類或者對象添加行爲。java
一是使用繼承。繼承是給一個類添加行爲的比較有效的途徑。經過使用繼承,可使得子類在擁有自身方法的同時,還能夠擁有父類的方法。可是使用繼承是靜態的,在編譯的時候就已經決定了子類的行爲,咱們不便於控制增長行爲的方式和時機。ide
二是使用關聯。組合即將一個對象嵌入到另外一個對象中,由另外一個對象來決定是否引用該對象來擴展本身的行爲。這是一種動態的方式,咱們能夠在應用程序中動態的控制。測試
與繼承相比,關聯關係的優點就在於不會破壞類的封裝性,且具備較好的鬆耦合性,可使系統更加容易維護。可是它的缺點就在於要建立比繼承更多的對象。this
1、基本定義 編碼
裝飾者模式,動態地將責任附加到對象上。若要擴展功能,裝飾者提供了比繼承更加有彈性的替代方案。spa
2、模式結構 .net
裝飾者模式UML結構圖。設計
Component: 抽象構件。是定義一個對象接口,能夠給這些對象動態地添加職責。調試
ConcreteComponent:具體構件。是定義了一個具體的對象,也能夠給這個對象添加一些職責。orm
Decorator: 抽象裝飾類。是裝飾抽象類,繼承了Component,從外類來擴展Component類的功能,但對於Component來講,是無需知道Decorator存在的。
ConcreteDecorator:具體裝飾類,起到給Component添加職責的功能。
3、實現裝飾者模式
情景模式:星巴茲以擴張速度快而聞名。在裏面購買咖啡時,能夠要求在其中加入各類調料,星巴茲會根據所加入的調料收取不一樣的費用,也就是說不一樣的咖啡與調料之間有N多不一樣的組合方式。每種咖啡和調料都有不一樣的收費。若是這個時候咱們使用繼承方式,則會陷入無以復加的地步。這裏會有N多個類,出現「類爆炸」現象。
結構圖以下:
裝飾者模式提供了一個比較好的解決方案。
編碼實現:
Component Beverage.java
[java] view plaincopyprint?
public abstract class Beverage {
protected String description = "Unknown Beverage";
public String getDescription() {
return description;
}
public abstract double cost();
}
四個組件:HouseBlend.java
[java] view plaincopyprint?
public class HouseBlend extends Beverage {
public HouseBlend(){
description = "HouseBlend";
}
@Override
public double cost() {
return 0.89;
}
}
DarkRoast.java
[java] view plaincopyprint?
public class DarkRoast extends Beverage {
public DarkRoast(){
description = "DarkRoast";
}
@Override
public double cost() {
return 1.05;
}
}
Espresso.java
[java] view plaincopyprint?
public class DarkRoast extends Beverage {
public DarkRoast(){
description = "DarkRoast";
}
@Override
public double cost() {
return 1.05;
}
}
Decat.java
[java] view plaincopyprint?
public Decat(){
description = "Decat";
}
@Override
public double cost() {
return 0.99;
}
CondimentDecorator.java
[java] view plaincopyprint?
public abstract class CondimentDecorator extends Beverage{
public abstract String getDescription();
}
Milk.java
[java] view plaincopyprint?
public class Milk extends CondimentDecorator {
Beverage beverage;
public Milk(Beverage beverage){
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + " , Milk";
}
@Override
public double cost() {
return beverage.cost() + 0.3;
}
}
Mocha.java
[java] view plaincopyprint?
public class Mocha extends CondimentDecorator {
Beverage beverage;
public Mocha(Beverage beverage){
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + " , Mocha";
}
@Override
public double cost() {
return beverage.cost() + 0.20;
}
}
Soy.java
[java] view plaincopyprint?
public class Soy extends CondimentDecorator{
Beverage beverage;
public Soy(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + " , Soy";
}
@Override
public double cost() {
return beverage.cost() + 0.10;
}
}
Whip.java
[java] view plaincopyprint?
public class Whip extends CondimentDecorator {
Beverage beverage;
public Whip(Beverage beverage){
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + " , Whip";
}
@Override
public double cost() {
return beverage.cost() + 0.20;
}
}
測試程序
[java] view plaincopyprint?
public class StarbuzzCoffee {
/**
* @param args
*/
public static void main(String[] args) {
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription() + " $" + beverage.cost());
Beverage beverage2 = new DarkRoast();
beverage2 = new Mocha(beverage2);
beverage2 = new Mocha(beverage2);
beverage2 = new Whip(beverage2);
System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
}
}
運行結果
4、裝飾者模式的優缺點
優勢
一、裝飾者模式能夠提供比繼承更多的靈活性
二、能夠經過一種動態的方式來擴展一個對象的功能,在運行時選擇不一樣的裝飾器,從而實現不一樣的行爲。
三、經過使用不一樣的具體裝飾類以及這些裝飾類的排列組合,能夠創造出不少不一樣行爲的組合。可使用多個具體裝飾類來裝飾同一對象,獲得功能更爲強大的對象。
四、具體構件類與具體裝飾類能夠獨立變化,用戶能夠根據須要增長新的具體構件類和具體裝飾類,在使用時再對其進行組合,原有代碼無須改變,符合「開閉原則」。
缺點
一、會產生不少的小對象,增長了系統的複雜性
二、這種比繼承更加靈活機動的特性,也同時意味着裝飾模式比繼承更加易於出錯,排錯也很困難,對於屢次裝飾的對象,調試時尋找錯誤可能須要逐級排查,較爲煩瑣。
五、裝飾者模式的適用場景
一、在不影響其餘對象的狀況下,以動態、透明的方式給單個對象添加職責。
二、須要動態地給一個對象增長功能,這些功能也能夠動態地被撤銷。 當不能採用繼承的方式對系統進行擴充或者採用繼承不利於系統擴展和維護時。
6、總結
一、 組合和委託能夠在運行時動態的添加新的行爲,而繼承是靜態的,在系統編譯時就已經決定了對象的行爲。
二、裝飾者模式意味着一羣裝飾者類,這些類用來包裝具體組件
三、裝飾者能夠在被裝飾者的行爲前面或者後面加上本身的行爲,甚至能夠將被裝飾者的行爲整個取代掉,從而達到特定的目的。
四、能夠用多個裝飾者包裝一個組件。
五、裝飾者通常對於組件的客戶是透明的,除非客戶程序依賴於組件的具體類型。
六、裝飾者會致使設計中出現許多的小對象,若是過分的使用,會讓系統變得更加複雜。
七、裝飾者和被裝飾者對象有相同的超類型。
圖以下:
Source類是被裝飾類,Decorator類是一個裝飾類,能夠爲Source類動態的添加一些功能,代碼以下:
[java] view plaincopy
public interface Sourceable {
public void method();
}
[java] view plaincopy
public class Source implements Sourceable {
@Override
public void method() {
System.out.println("the original method!");
}
}
[java] view plaincopy
public class Decorator implements Sourceable {
private Sourceable source;
public Decorator(Sourceable source){
super();
this.source = source;
}
@Override
public void method() {
System.out.println("before decorator!");
source.method();
System.out.println("after decorator!");
}
}
測試類:
[java] view plaincopy
public class DecoratorTest {
public static void main(String[] args) {
Sourceable source = new Source();
Sourceable obj = new Decorator(source);
obj.method();
}
}
輸出:
before decorator!
the original method!
after decorator!
裝飾器模式的應用場景:
一、須要擴展一個類的功能。
二、動態的爲一個對象增長功能,並且還能動態撤銷。(繼承不能作到這一點,繼承的功能是靜態的,不能動態增刪。)
缺點:產生過多類似的對象,不易排錯!