同系列文章: 從未這麼明白的設計模式(二):觀察者模式
從未這麼明白的設計模式(一):單例模式
首先我們列舉乙個案例,並且按照物件導向的思想來對應實體之間的關係。
有乙個咖啡店,銷售各種各樣的咖啡,拿鐵,卡布奇洛,藍山咖啡等,在沖泡前,會詢問顧客是否要加糖,加奶,加薄荷等。這樣不同的咖啡配上不同的調料就會賣出不同的**。
針對上面的栗子,我們很容易就抽象出對應的實現,如上圖。接著,我們就要編寫對應的類來實現對應的功能。在這個例子中,主題當然就是咖啡
,並且它有乙個屬性是名字
,乙個行為**
,出於「物件導向」的思想,我們自然會設計出抽象類coffee
:
public
abstract
class
coffee
複製**
接著,按照繼承的思想,我們要開始設計出具體的實現類,因為拿鐵,卡布奇洛,藍山搭配上不同的調料(上面三種)會有不同的**,名字,所以我們至少得設計出 3 x 3 = 9 個類來分別對應它們的名字和**:
嗯!我想不用說這樣設計得缺陷也很明顯了! 由於不同的咖啡和不同的調料得各種任意組合,使得出現了類**
的現象。既然有這麼明顯的缺陷,那我們當然得改! 我們可以考慮把各種調料當作屬性加入到coffee這個抽象類中,接著在實現類中計算**和名字時,分別判斷是否加入了各種調料包,得到不同的名字和**!
按照上面的思想,我們的coffee類現在變成了這樣:
public
abstract
class
coffee
複製**
接著,我們實現一種咖啡,藍山咖啡:
public
class
bulecoffee
extends
coffee
if (addedmilk)
if (addedsugar)
return name.tostring();
}@override
public
double
getprice
() if (addedmilk)
if (addedsugar)
return price;
}}複製**
嗯!現在似乎比上面愉快多了。其實不然!我們仔細分析這種設計,會發現它似乎不太符合」封裝的思想「,比如說針對拿鐵,對於加薄荷而言,對他總是多餘的! 而對於藍山而言,牛奶又顯得很多餘! 所以這種設計也並不合理。 另外,我們假設coffee,拿鐵等實體類來自第三方類庫,我們並不能改動這些類的實現, 又要怎麼得到名字和**呢?
這個時候,我們就得使用裝飾器
模式來動態的擴充套件類行為! 所以我們設計出v3版本。
首先,我們需要了解乙個物件導向的乙個基本設計原則:開閉原則
,它指的是類應該對修改關閉,對擴充套件開放
。
怎麼理解呢? 就比如我們上方說的:假如cofee和它的一眾實現拿鐵,卡布奇洛,藍山來自第三方類庫,並且這個類庫已經很」適合「,」實用「了。 而我們為了得到加入不同調料的咖啡的名字和**,我們就得修改這些實現,而這樣的修改,總是免不了穩定性
的改變。對原本的系統來說也是一種風險! 所以我們應該對修改關閉,對擴充套件開放
;
遵循開閉原則,那我們就得對外擴充套件,那怎麼對外擴充套件呢? 這也是裝飾器模式實現的關鍵,利用繼承和組合
的結合; 現在我們可以考慮設計出乙個裝飾類,它也繼承自coffee,並且它內部有乙個coffee的例項物件:
現在,我們多了乙個咖啡裝飾器
: coffeedecorator:
public
abstract
class
coffeedecorator
implements
coffee
@override
public string getname
() @override
public
double
getprice()}
複製**
接著,我們將牛奶,薄荷作為抽象出乙個類,繼承自coffeedecorator,所以,現在類圖就成了這樣:
我們實現乙個milkcoffeedecorator
:
public
class
milkcoffeedecorator
extends
coffeedecorator
@override
public string getname
() @override
public
double
getprice()}
複製**
按同樣的方法可以實現出mintcoffeedecorator
,sugarcoffeedecorator
。接著我們寫乙個測試類:
public
class
}複製**
從結果我們可以看出,隨著不斷加入各種調料,**,名字都在改變! 這說明我們加入不同的調料,動態的改變了咖啡的名字和**!
從上面的最後的裝飾器模式的實現來看,我們可以得出以下結論:
通過裝飾器模式可以動態的將責任附加到原有的物件上,而不改變原有的code。
遵循開閉原則
裝飾者和被裝飾者有相同的父類(如栗子中的coffee)
可以用多個裝飾器裝飾同乙個物件。(見執行類)
裝飾者可以在被裝飾者的行為之前或之後動態的加上自己的行為。(參考裝飾實現)
組合比繼承更加的靈活(上面的coffee**)
到現在,我們已經實現了乙個自己的裝飾器,我們來看看jdk中用到的裝飾器實現.
我們可以檢視filterinputstream:
它的主要是實現者為bufferedinputstream
:
所以我們經常可以使用bufferedinputstream裝飾乙個inputstream,比如fileinputstream:new bufferedinputstream(fileinputstream);
這就是裝飾器模式的典型應用。
在tomcat的httpservletrequest的內部實現**中,requestfacde
繼承自httpservlet,而它內部的實現也是通過**request
物件,而request物件繼承自httpservlet,request內部**了org.apache.coyote.request
來實現的。
裝飾器模式充分展示了組合的靈活。利用它來實現擴充套件。它同時也是開閉原則的體現。 如果相對某個類實現執行時功能動態的擴充套件。 這個時候你就可以考慮使用裝飾者模式!
關注我,這裡只有乾貨!
設計模式原來這麼簡單 裝飾器模式
1.什麼是裝飾器模式wiki 允許將行為靜態或動態地新增到單個物件中,而不會影響同一類中其他物件的行為。2.例項 以前用的qq秀,需要為角色穿不同的衣服 頭飾 臉飾 上衣 褲子。不同的衣服展示不同的形象,而且還可以 根據形象的炫酷值和好友pk。3.程式示例 qq秀角色 public inte ce ...
設計模式 三 裝飾器模式
裝飾器模式,動態地給乙個物件新增一些額外的職責,就增加功能來說,裝飾模式比生成子類更靈活。它把每個要裝飾的功能放在單獨的類中,並讓這個類包裝它所要裝飾的物件,因此,當需要執行特殊行為時,客戶 可以在執行時根據需要有選擇地,有根據地使用裝飾功能包裝物件。被裝飾物件基類 public inte ce c...
深入設計模式 裝飾器模式(三)
裝飾器模式 就是在原來的基礎上新增新的功能,但不改變其結構 什麼叫裝飾器?就是用來包裝某個東西的稱呼,比如,衣服上有個帽子,這個帽子就是衣服的裝飾 首先有個自己的基本資訊介紹,因為你要讓對方有所了解啊,這樣聊起來才不會尷尬是吧。然後實現介紹一下自己的基本資訊。然後就這樣開始相親了,美女一看,恩。小夥...