C#設計模式-裝飾器模式(Decorator Pattern)

引言

當咱們完成一個軟件產品開發後就須要對其進行各類測試,適配快速迭代下質量的保障。當有一個完善的產品的對象後,若是咱們想要給他添加一個測試功能,那麼咱們能夠用一個新的類去裝飾它來實現對原有對象職責的擴展。新的類稱爲「裝飾者」,原有的對象稱爲「被裝飾者」。這種模式被稱爲裝飾器模式。設計模式

概念

裝飾器模式(Decorator Pattern)容許向一個現有的對象添加新的功能,同時又不改變其結構。這種類型的設計模式屬於結構型模式,它是做爲現有的類的一個包裝。
這種模式建立了一個裝飾類,用來包裝原有的類,並在保持類方法簽名完整性的前提下,提供了額外的功能。
咱們經過下面的實例來演示裝飾器模式的用法。其中,咱們將把一個形狀裝飾上不一樣的顏色,同時又不改變形狀類。ide

結構圖

裝飾器模式中的角色:測試

  • 抽象構件(Component)角色:聲明封裝器和被封裝對象的公用接口。即給出一個抽象接口,已規範準備接收附加責任的對象。
  • 具體構件(ConcreteComponent)角色:類是被封裝對象所屬的類。 它定義了基礎行爲, 但裝飾類能夠改變這些行爲。
  • 裝飾(Decorator)角色:擁有一個指向被封裝對象的引用成員變量。 該變量的類型應當被聲明爲通用部件接口, 這樣它就能夠引用具體的部件和裝飾。 裝飾基類會將全部操做委派給被封裝的對象。
  • 具體裝飾(ConcreteDecorator)角色:定義了可動態添加到部件的額外行爲。 具體裝飾類會重寫裝飾基類的方法, 並在調用父類方法以前或以後進行額外的行爲。負責給構件對象「貼上」附加的責任。

實現

實現一個開發完成後的產品,對其進行手工功能測試、自動化測試、壓力測試。將產品做爲被裝飾者,也就是構件。各類測試做爲裝飾者,計算附加不一樣的測試花費的總時間。this

實現思路:

  • 定義一個產品抽象類。
  • 實現具體的產品,具體的產品繼承產品抽象類。
  • 定義一個測試類型的抽象裝飾類,繼承產品抽象類。
  • 實現不一樣類型的測試,繼承測試類型的抽象裝飾類。
  • 使用時實例化一個產品,而後對產品進行附件不一樣的測試類型。
using System;


namespace Decorator
{
    class Program
    {
        static void Main(string[] args)
        {
            Product a = new ProductA();
            Console.WriteLine($"未執行{a.Test}測試時總共花費時間{a.TotalTime}");
            a = new ManualTest(a);
            Console.WriteLine($"執行{a.Test}總共花費時間{a.TotalTime}");
            Console.WriteLine("=====================================");

            Product b = new ProductB();
            b = new ManualTest(b);
            b = new StressTest(b);
            b = new AutoTest(b);
            Console.WriteLine($"執行{b.Test}總共花費時間{b.TotalTime}");

            Console.Read();
        }
    }

    /// <summary>
    /// 一個項目產品,抽象構件
    /// </summary>
    public abstract class Product
    {
        public string Name { get; set; }
        public double SpendTime { get; set; }
        public abstract string Test { get; }
        public abstract double TotalTime { get; }
    }

    /// <summary>
    /// 具體的項目產品A,具體構件
    /// </summary>
    public class ProductA : Product
    {
        public ProductA()
        {
            Name = "ProductA";
            SpendTime = 0;
        }

        public override string Test => this.Name;
        public override double TotalTime => this.SpendTime;
    }

    /// <summary>
    /// 具體的項目產品B,具體構件
    /// </summary>
    public class ProductB : Product
    {
        public ProductB()
        {
            Name = "ProductB";
            SpendTime = 0;
        }

        public override string Test => this.Name;
        public override double TotalTime => this.SpendTime;
    }

    /// <summary>
    /// 測試類型,抽象裝飾
    /// </summary>
    public abstract class TestType : Product
    {
        Product _product = null;

        public TestType(Product product)
        {
            _product = product;
        }

        public override string Test
        {
            get
            {
                return _product.Test + "+" + this.Name;
            }
        }

        public override double TotalTime
        {
            get
            {
                return _product.TotalTime + this.SpendTime;
            }
        }
    }

    /// <summary>
    /// 手工測試類型,具體裝飾
    /// </summary>
    public class ManualTest : TestType
    {

        public ManualTest(Product product) : base(product)
        {
            Name = "手工測試";
            SpendTime = 200;
        }
    }

    /// <summary>
    /// 自動化測試類型,具體裝飾
    /// </summary>
    public class AutoTest : TestType
    {

        public AutoTest(Product product) : base(product)
        {
            Name = "自動化測試";
            SpendTime = 100;
        }

    }

    /// <summary>
    /// 壓力測試類型,具體裝飾
    /// </summary>
    public class StressTest : TestType
    {
        public StressTest(Product product) : base(product)
        {
            Name = "壓力測試";
            SpendTime = 200;
        }
    }

}

運行後結果:spa

未執行ProductA測試時總共花費時間0
執行ProductA+手工測試總共花費時間200
=====================================
執行ProductB+手工測試+壓力測試+自動化測試總共花費時間500

應用場景

  • 在無需修改代碼的狀況下便可使用對象, 且但願在運行時爲對象新增額外的行爲時可使用裝飾模式。由於裝飾能將業務邏輯組織爲層次結構,可爲各層建立一個裝飾, 在運行時將各類不一樣邏輯組合成對象。 因爲這些對象都遵循通用接口, 客戶端代碼能以相同的方式使用這些對象。
  • 若是用繼承來擴展對象行爲的方案難以實現或者根本不可行,可使用裝飾模式。

優缺點

優勢

  • 無需建立新子類便可擴展對象的行爲。
  • 能夠在運行時添加或刪除對象的功能。
  • 能夠用多個裝飾封裝對象來組合幾種行爲。
  • 裝飾類和被裝飾類能夠獨立發展,不會相互耦合。
  • 單一職責原則。 能夠將實現了許多不一樣行爲的一個大類拆分爲多個較小的類。

缺點

  • 在封裝器棧中刪除特定封裝器比較困難。
  • 實現行爲不受裝飾棧順序影響的裝飾比較困難。
  • 各層的初始化配置代碼看上去可能會很糟糕。
相關文章
相關標籤/搜索