動态的給一個對象添加一個額外的職責,就增加功能來說,裝飾模式比生成子類更為靈活。
——獎金計算
不用模式的解決方案
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<code>public</code> <code>class</code> <code>Prize {</code>
<code> </code><code>public</code> <code>double</code> <code>calcPrize(String user, Date begin, Date end) {</code>
<code> </code><code>double</code> <code>prize = </code><code>0.0</code><code>;</code>
<code> </code><code>// 計算當月業務獎金,所有人都會計算</code>
<code> </code><code>prize = </code><code>this</code><code>.monthPrize(user, begin, end);</code>
<code> </code><code>// 計算累計獎金</code>
<code> </code><code>prize += </code><code>this</code><code>.calcPrize(user, begin, end);</code>
<code> </code><code>// 需要判斷該人員是普通人員還是業務經理,團隊獎金隻有業務經理才有</code>
<code> </code><code>if</code><code>(</code><code>this</code><code>.isManager(user)) {</code>
<code> </code><code>prize += </code><code>this</code><code>.groupPrize(user, begin, end);</code>
<code> </code><code>}</code>
<code> </code><code>return</code> <code>prize;</code>
<code> </code><code>}</code>
<code> </code><code>private</code> <code>double</code> <code>groupPrize(String user, Date begin, Date end) {</code>
<code> </code><code>return</code> <code>0</code><code>;</code>
<code> </code><code>private</code> <code>boolean</code> <code>isManager(String user) {</code>
<code> </code><code>return</code> <code>false</code><code>;</code>
<code> </code><code>private</code> <code>double</code> <code>monthPrize(String user, Date begin, Date end) {</code>
<code>}</code>
使用裝飾模式來解決問題
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<code>public</code> <code>abstract</code> <code>class</code> <code>Component {</code>
<code> </code><code>public</code> <code>abstract</code> <code>double</code> <code>calcPrize(String user, Date begin, Date end);</code>
<code>public</code> <code>abstract</code> <code>class</code> <code>Decorator </code><code>extends</code> <code>Component {</code>
<code> </code><code>protected</code> <code>Component c;</code>
<code> </code><code>public</code> <code>Decorator(Component c) {</code>
<code> </code><code>super</code><code>();</code>
<code> </code><code>this</code><code>.c = c;</code>
<code> </code><code>// 轉調元件對象的方法</code>
<code> </code><code>return</code> <code>c.calcPrize(user, begin, end);</code>
<code>public</code> <code>class</code> <code>MonthPrizeDecorator </code><code>extends</code> <code>Decorator {</code>
<code> </code><code>public</code> <code>MonthPrizeDecorator(Component c) {</code>
<code> </code><code>super</code><code>(c);</code>
<code> </code><code>// 先擷取前面運算出來的獎金</code>
<code> </code><code>double</code> <code>money = </code><code>super</code><code>.calcPrize(user, begin, end);</code>
<code> </code><code>// 然後計算當月業務獎金</code>
<code> </code><code>return</code> <code>money + prize;</code>
<code>public</code> <code>class</code> <code>SumPrizeDecorator </code><code>extends</code> <code>Decorator {</code>
<code> </code><code>public</code> <code>SumPrizeDecorator(Component c) {</code>
<code> </code><code>// 然後計算累計獎金</code>
<code> </code><code>double</code> <code>prize = </code><code>1000000</code> <code>* </code><code>0.001</code><code>;</code>
<code>public</code> <code>class</code> <code>Client {</code>
<code> </code><code>public</code> <code>static</code> <code>void</code> <code>main(String[] args) {</code>
<code> </code><code>Component c1 = </code><code>new</code> <code>ConcreteComponent();</code>
<code> </code><code>Decorator d1 = </code><code>new</code> <code>MonthPrizeDecorator(c1);</code>
<code> </code><code>Decorator d2 = </code><code>new</code> <code>SumPrizeDecorator(d1);</code>
<code> </code><code>double</code> <code>userA = d1.calcPrize(</code><code>"a"</code><code>, </code><code>null</code><code>, </code><code>null</code><code>);</code>
<code> </code><code>double</code> <code>userB = d2.calcPrize(</code><code>"b"</code><code>, </code><code>null</code><code>, </code><code>null</code><code>);</code>
案例說明:
<a target="_blank" href="http://blog.51cto.com/attachment/201306/170404990.png"></a>
由于獎金的計算方式經常發生變動,幾乎每個季度都有小調整,每年都有大調整,這要求軟體實作要足夠靈活,能夠很快進行相應的調整和修改,否則就不能滿足實際業務的需要。把問題抽象以下:設若有一個計算獎金的對象,現在需要能夠靈活的給它增加和減少功能,還需要能夠動态的組合功能,每個功能就相當于在計算獎金的某個部分。
則問題就是:如何能透明地給一個對象增加功能,并實作功能的動态組合。
解決思路
所謂透明地給一個對象增加功能,即給一個對象增加功能,但不能讓這個對象知道,也就不是不去改動這個對象,而實作給一個對象透明的增加功能,這就實作了功能的動态組合。
實作透明地給一個對象增加功能,即要擴充對象功能,可使用繼承。
為了能夠實作和原來使用被裝飾對象的代碼無縫結合,通過頂一個抽象類,讓這個類實作與被裝飾對象相同的接口,然後在具體實作類中,轉調被裝飾的獨享,在轉調前後添加新的功能,這就實作了給裝飾對象增加功能。
在轉調的時候,如果覺得被裝飾對象的功能不再被需要,還可以直接替換,也就是不在轉調,而是在裝飾杜相忠完成全新的實作。
示例代碼
<code> </code><code>public</code> <code>abstract</code> <code>void</code> <code>operation();</code>
<code>public</code> <code>class</code> <code>ConcreteComponent </code><code>extends</code> <code>Component {</code>
<code> </code><code>public</code> <code>void</code> <code>operation() {</code>
<code> </code><code>// 相應的功能處理</code>
<code> </code><code>protected</code> <code>Component component;</code>
<code> </code><code>public</code> <code>Decorator(Component component) {</code>
<code> </code><code>this</code><code>.component = component;</code>
<code> </code><code>// 轉發請求給元件獨享,可以在轉發前後執行一些附加動作</code>
<code> </code><code>component.operation();</code>
<code>public</code> <code>class</code> <code>ConcreteDecoratorA </code><code>extends</code> <code>Decorator {</code>
<code> </code><code>public</code> <code>ConcreteDecoratorA(Component component) {</code>
<code> </code><code>super</code><code>(component);</code>
<code> </code><code>private</code> <code>String addedState;</code>
<code> </code><code>// 調用父類的方法,可以在調用前後執行一些附加動作</code>
<code> </code><code>// 在這裡進行處理的時候,可以使用添加的狀态</code>
<code> </code><code>super</code><code>.operation();</code>
<code>public</code> <code>class</code> <code>ConcreteDecoratorB </code><code>extends</code> <code>Decorator {</code>
<code> </code><code>public</code> <code>ConcreteDecoratorB(Component component) {</code>
<code> </code><code>private</code> <code>void</code> <code>addBehavior() {</code>
<code> </code><code>// 需要添加的職責實作</code>
<code> </code><code>this</code><code>.addBehavior();</code>
應用範圍
裝飾模式能夠實作動态添加,是從一個對象外部來給對象添加功能,相當于改變了對象的外觀。當裝飾後,從外部使用系統的角度,就不再是使用原來是的那個類,而是使用被一系列裝飾器包裝過後的對象。
對象組合
在面向對象的設計中,有一條基本規則就是"盡量使用對象組合,而不是繼承"來擴充和複用功能。裝飾模式就是這個規則。
假如有一個對象A,實作了a1方法,而C1對象想要擴充A的功能,給它增加一個c1的方法。
使用繼承:
<code>public</code> <code>class</code> <code>A {</code>
<code> </code><code>public</code> <code>void</code> <code>a1() {}</code>
<code>public</code> <code>class</code> <code>C </code><code>extends</code> <code>A {</code>
<code> </code><code>public</code> <code>void</code> <code>c1() {}</code>
使用對象組合:
<code>public</code> <code>class</code> <code>C {</code>
<code> </code><code>private</code> <code>A a = </code><code>new</code> <code>A();</code>
<code> </code><code>public</code> <code>void</code> <code>a1() {</code>
<code> </code><code>a.a1();</code>
裝飾器群組件類的關系
裝飾器是用來修飾元件的,裝飾器一定要實作群組件類一緻的接口。元件類是不知道裝飾器的存在的,裝飾器為元件提那家功能是一種透明的包裝。
裝飾器的應用—— I/O流
<a target="_blank" href="http://blog.51cto.com/attachment/201306/170501449.png"></a>
InputStream就相當于裝飾模式中的Component;
FileInputStream、ObjectInputStream、StringBufferInputStream這些對象都是直接繼承InputStream,這些相當于裝飾模式中的ConcreteComponent,是可以被裝飾器裝飾的對象;
FilterInputStream相當于裝飾器模式中的Decorator,而它的子類DataInputStream、BufferedInputStream、LinkNumberInputStream和PushbackInputStream 相當于裝飾器模式中的ConcreteDecorator。
裝飾模式的優缺點:
優點:比繼承更靈活、更容易複用功能、簡化高層定義
缺點:會産生很多細粒度對象
裝飾模式的本質是:動态組合
動态是手段,組合才是目的。這裡組合有兩個意思,一個是動态功能的組合,也就是動态進行裝飾器的組合;另一個是指對象組合,通過對象組合來實作為裝飾對象透明的增加功能。
何時選用裝飾模式模式
如果想:在不影響其他對象的情況下,以動态、透明的方式給對象添加職責,可以使用裝飾模式。
如果:不适合使用子類來擴充的時候,可以使用裝飾模式。
本文轉自 LinkedKeeper 51CTO部落格,原文連結:http://blog.51cto.com/sauron/1230136,如需轉載請自行聯系原作者