設計模式學習之裝飾(decorator)

JosLogan 7年前發布 | 4K 次閱讀 設計模式

最近工作上遇到一個問題,最后用設計模式——裝飾模式(Decorator)解決了,加深了對這個模式的印象,記錄一下,同時當作對看書的復習吧,如果對其他朋友有拋磚引玉的作用就最好了^_^。

問題描述

我們的UI操作是一種已經定下來的模式,后來被要求修改為另外一種操作模式。簡單來說,就是要改變一種控件的功能,我們后面就把這種控件叫做WidgetBox吧。而且,這個WidgetBox類本身有別的用途,暫時不考慮直接在原來的基礎上修改。

從這個角度來說,解決這個問題首先想到的方法是: 繼承! 以WidgetBox作為父類重新寫一個子類,重載關鍵函數,滿足新操作模式的需求。

但是,恰恰遇到問題了:

  • 子類需要把父類的主要功能完全重寫,需要改掉很多函數代碼,麻煩;
  • 還有另外一個問題。由于界面用的是flash,在消息處理方面綁定的比較底層,已經很難改動了,除非你把整個工程中別的地方用到的都改掉,工程上這是不切實際的;
  • 最重要的是:目前由于UI是flash制作的,所以在控件上有一些約定。新定義控件(反應到代碼里面也就是添加子類)雖然不麻煩,但是還要把一部分維護工作留給UI人員,是自己不想的。

所以, 繼承 這個方法行不通,會因為改動底層給系統帶來很大的不穩定性,而且代碼量和結構上的修改并沒有什么可取之處,如果需求變了,需要新的操作模式呢?

在以上的背景之下,自己想到了如此拙見:用到了裝飾(Decorator)這個設計模式。雖然不能完美解決問題,還存在缺點,但是總的來說比上面的這個方法好。如果以后再碰到更好的解決辦法,說明我又進步了。^_^

裝飾(Decorator)模式

引用書上的一句話:

動態地給一個對象添加一些額外的職責。

動機

依然引用書上的說法:

有時我們希望給某個對象而不是整個類添加一些功能。一種較為靈活的方式是將組件嵌入另一個對象中。

對接上自己需要的:

很簡單!能在不改變現有系統結構的情況下,增加一種新的操作方式。那么我就實現一個裝飾類,在其中嵌入已經存在的控件類WidgetBox,然后我在裝飾類中實現自己的各種操作方式,而且用到大部分的WidgetBox類本身已經實現的功能。

結構

完整的裝飾模式的結構如下圖所示:

但是,因為個中原因,自己的實現有些許不同:

說明一下:

  • 1.沒有實現一個上層的抽象接口類,因為不想改變現有的結構。但是寫到這里的時候,覺得Decorator類可以繼承Widget類(他是WidgetBox類最上層的基類),這樣會優化下面提到的問題。同時,這也說明記錄寫文章是有一些用處滴^_^。
  • 2.這個接口暴露了自己實現的一個缺點:Decorator需要完全擁有和WidgetBox一模一樣的接口,這里就留下了維護這兩個類接口一致的問題,特別是如果你想要用戶在WidgetBoxDecorator的操作和WidgetBox沒有區別的情況下。但是,上面也提到了可以繼承widget類,會大大減弱這個問題。

實現

### C++ class WidgetBox{ void subscribe(event); void unsubscribe(event); void HoldObject(); bool OnMousePress(); bool OnMouseUp(); }

class Decorator{
    virtual void subscribe(event);
    virtual void unsubscribe(event);
    virtual void HoldObject();
    virtual bool OnMousePress();
    virtual bool OnMouseUp();
private:
    WidgetBox* pItem;
}

class WidgetBoxDecorator{
    void subscribe(event);
    void unsubscribe(event);
    void HoldObject();
    bool OnMousePress();
    bool OnMouseUp();
}

int main()
{
    // 老的控件操作模式
    WidgetBox *pItem = new WidgetBox("name");
    pItem->HoldObject();

    // 新的控件操作模式
    WidgetBoxDecorator *pItem(new WidgetBox("name"));
    pItem->HoldObject();
} ### 小結
  • 什么時候用裝飾模式? > 1.在不影響其他對象的情況下,比如自己這里不能夠改變現有的結構; > 2.當不能采用生成子類的方法進行擴充時,自己碰到的問題也算是; > 3.書上還提到:處理那些可以撤銷的職責,這個沒有用到。

總結

自己也把《設計模式-可服用面向對象軟件的基礎》這本書看了一遍,印象其實不是很深刻,除了個別非常形象的模式之外,例如:享元模式-FlyWeight。剩下的就是自己去寫過或者常用的模式了,比如:單例模式、工廠模式、觀察者模式。所以,對于自己來說,碰到一個問題,特別是設計上的問題,然后用一個設計模式去解決這個問題,才能深刻理解這個模式的優缺點,以及用途。

參考

[1]《設計模式:可復用面向對象軟件的基礎》

學習:JavaScript實現

var WidgetBox = {
    createNew: function(){
        var widget = {};
        widget.OnMouseUp = function()
        {
            console.log("WidgetBox OnMouseUp();");
        }
        return widget;
    }
}

var Decorator = {
    createNew: function(){  
        var widget = {};
        widget.item = WidgetBox.createNew();
        widget.OnMouseUp = function()
        {
            item.OnMouseUp();
            console.log("Decorator OnMouseUp();");
        }
        return widget;
    }
}

var WidgetBoxDecorator = {
    createNew: function(){
        // 繼承
        var widget = Decorator.createNew();
        widget.item = WidgetBox.createNew();
        widget.OnMouseUp = function()
        {
            console.log("WidgetBoxDecorator OnMouseUp();");
        }
        return widget;
    }
}

// old operation
console.log("Old operation:");
var item = WidgetBox.createNew();
item.OnMouseUp();

// new operation
console.log("New operation:");
var item1 = WidgetBoxDecorator.createNew();
item1.OnMouseUp();

結果:

Old operation:
WidgetBox OnMouseUp();
New operation:
WidgetBoxDecorator OnMouseUp();

 

來自:http://pkxpp.github.io/2017/02/12/設計模式學習之裝飾(Decorator)/

 

 本文由用戶 JosLogan 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!