設計模式的征途—10.裝飾(Decorator)模式

雖然目前房價依舊很高,就連我所在的成都郊區(非中心城區)的房價均價都早已破萬,但卻仍是阻擋不了你們對新房的渴望和買房的熱情。若是你們買的是清水房,那麼無疑還有一項艱鉅的任務在等着你們,那就是裝修。對新房的裝修並無改變房屋用於居住的本質,但它可讓房子變得更加漂亮和舒適以及更加實用。在軟件設計中,也有一種相似於新房裝修的技術能夠對已有的功能進行擴展使之更加符合用戶需求,從而使得對象具備更增強大的功能,這即是本次即將介紹的裝飾模式。設計模式

裝飾模式(Decorator) 學習難度:★★★☆☆ 使用頻率:★★★☆☆

1、圖形界面構件庫設計

1.1 需求背景

背景:M公司開發部基於OO技術開發了一套圖形界面構件庫Visual Component,該構件庫提供了大量的基本構件,如窗體、文本框、列表框等等,因爲在使用該構件庫時,用戶常常要求定製一些特殊的顯示效果,例如帶滾動條的窗體,帶黑色邊框的文本框,即帶滾動條又帶黑色邊框的列表框等,所以常常須要對該構件庫進行擴展以加強其功能,以下圖所示:ide

  如何提升圖形界面構件庫的可擴展性並下降其維護成本是M公司開發部的程序猿們必需要面對的一個問題。學習

1.2 初始設計

  M公司的開發人員針對上面的需求,提出了一個基於繼承複用的初始設計方案,其基本結構以下圖所示:測試

  經過分析該設計方案,不難發現存在如下問題:this

  (1)系統擴展麻煩,在C#/Java中根本沒法實現(不支持多繼承)。spa

  (2)代碼重複,不利於對系統進行修改和維護。設計

  (3)系統龐大,類的數量很是多。調試

  總之,這個設計不是一個好的設計方案,如何讓系統利於擴展又不致使類的數量線性增長呢?讓咱們瞭解一下裝飾類把。code

2、裝飾模式概述

2.1 裝飾模式簡介

  裝飾模式能夠在不改變一個對象自己功能的基礎上給對象增長額外的新行爲,在現實生活中,這種狀況也處處存在,例如一張照片,能夠不改變照片自己,給它增長一個相框,使得它具備防潮的功能,並且用戶能夠根據須要給它增長不一樣類型的相框,甚至能夠在一個小相框的外面再套一個大相框。component

裝飾(Decorator)模式:動態地給一個對象增長一些額外的職責,就增長對象功能來講,裝飾模式遠比生成子類實現更加靈活。裝飾模式是一種對象結構型模式  

2.2 裝飾模式結構

  從結構圖中能夠看出,裝飾模式主要有如下幾個角色:

  (1)Component (抽象構件):具體構件和抽象裝飾類的基類,聲明瞭在具體構建中實現的業務方法。

  (2)ConcreteComponent(具體構件):抽象構件的子類,用於定義具體的構件對象,實現了在抽象構件中聲明的方法,裝飾器能夠給它增長額外的職責(方法)。

  (3)Decorator(抽象裝飾類):它也是抽象構件類的子類,用於給具體構件增長職責,可是具體職責在其子類中實現。

  (4)ConcreteDecorator(具體裝飾類):抽象裝飾類的子類,負責向構件添加新的職責。

3、重構圖形界面構件庫

3.1 重構後的設計方案

  爲了讓系統具備更好的靈活性和可擴展性,克服繼承複用所帶來的問題,M公司開發人員使用裝飾模式來重構圖形界面庫的設計,其中部分類的基本結構以下圖所示:

  其中,Component充當抽象構件類,其子類Window、TextBox和ListBox充當具體構件類,ComponentDecorator則充當抽象裝飾類,ScrollBarDecorator和BlackBorderDecorator則充當具體裝飾類。

3.2 重構後的代碼實現

  (1)抽象構件:Component

    /// <summary>
    /// 抽象界面構件類:抽象構件類
    /// </summary>
    public abstract class Component
    {
        public abstract void Display();
    }

  (2)具體構件:Window, TextBox 和 ListBox

    /// <summary>
    /// 窗體類:具體構件類
    /// </summary>
    public class Window : Component
    {
        public override void Display()
        {
            Console.WriteLine("顯示窗體!");
        }
    }

    /// <summary>
    /// 文本框類:具體構件類
    /// </summary>
    public class TextBox : Component
    {
        public override void Display()
        {
            Console.WriteLine("顯示文本框!");
        }
    }

    /// <summary>
    /// 列表框類:具體構件類
    /// </summary>
    public class ListBox : Component
    {
        public override void Display()
        {
            Console.WriteLine("顯示列表框!");
        }
    }

  (3)抽象裝飾:ComponentDecorator

    /// <summary>
    /// 構件裝飾類:抽象裝飾類
    /// </summary>
    public class ComponentDecorator : Component
    {
        private Component component;

        public ComponentDecorator (Component component)
        {
            this.component = component;
        }

        public override void Display()
        {
            component.Display();
        }
    }

  (4)具體裝飾:ScrollBarDecorator 和 BlackBorderDecorator

    /// <summary>
    /// 滾動條裝飾類:具體裝飾類
    /// </summary>
    public class ScrollBarDecorator : ComponentDecorator
    {
        public ScrollBarDecorator(Component component) : base(component)
        {

        }

        public override void Display()
        {
            this.SetScrollBar();
            base.Display();
        }

        public void SetScrollBar()
        {
            Console.WriteLine("爲構件增長滾動條!");
        }
    }

    /// <summary>
    /// 黑色邊框裝飾類:具體裝飾類
    /// </summary>
    public class BlackBorderDecorator : ComponentDecorator
    {
        public BlackBorderDecorator(Component component) : base(component)
        {

        }

        public override void Display()
        {
            this.SetScrollBar();
            base.Display();
        }

        public void SetScrollBar()
        {
            Console.WriteLine("爲構件增長黑色邊框!");
        }
    }

  (5)客戶端測試

    public class Program
    {
        public static void Main(string[] args)
        {
            Component component = new Window();
            // 一次裝飾
            Component componentSB = new ScrollBarDecorator(component);
            componentSB.Display();

            Console.WriteLine();
            // 二次裝飾
            Component componentBB = new BlackBorderDecorator(componentSB);
            componentBB.Display();

            Console.ReadKey();
        }
    }

  執行後的結果以下圖所示:

  

  能夠看到,第一次裝飾以後,窗體有了滾動條。第二次裝飾以後,窗體不只有了滾動條,還增長了黑色邊框。

4、裝飾模式小結

4.1 主要優勢

  (1)對於擴展一個對象的功能,裝飾模式比繼承更加靈活 => 不會致使類的個數急劇增長!

  (2)能夠對一個對象進行屢次裝飾,從而創造出不少不一樣行爲的組合 => 獲得功能更爲強大的對象!

  (3)具體構件類與具體裝飾類能夠獨立變化,能夠根據須要增長新的具體構建和具體裝飾 => 原有代碼無需修改,符合開放封閉原則!

4.2 主要缺點

  雖然裝飾模式拱了一種比繼承更加靈活機動的方案,但同時也意味着比繼承更加易於出錯,排錯也很困難。特別是通過屢次裝飾的對象,調試時尋找錯誤可能須要逐級排查,較爲繁瑣

4.3 應用場景

  (1)在不影響其餘對象的狀況下,想要動態地、透明地給單個對象添加職責 => 採用裝飾模式吧!

  (2)當不能採用繼承的方式對系統進行擴展 或 採起繼承不利於系統擴展和維護時 => 採用裝飾模式吧!

參考資料

  DesignPattern

  劉偉,《設計模式的藝術—軟件開發人員內功修煉之道》

 

相關文章
相關標籤/搜索