天天看點

5.[研磨設計模式筆記]裝飾模式 1.定義 2.解決問題 3.模式講解 4.思考

動态的給一個對象添加一個額外的職責,就增加功能來說,裝飾模式比生成子類更為靈活。

——獎金計算

不用模式的解決方案

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,如需轉載請自行聯系原作者

繼續閱讀