引言
當我們完成一個軟體產品開發后就需要對其進行各種測驗,適配快速迭代下質量的保障,當有一個完善的產品的物件后,如果我們想要給他添加一個測驗功能,那么我們可以用一個新的類去裝飾它來實作對原有物件職責的擴展,新的類稱為“裝飾者”,原有的物件稱為“被裝飾者”,這種模式被稱為裝飾器模式,
概念
裝飾器模式(Decorator Pattern)允許向一個現有的物件添加新的功能,同時又不改變其結構,這種型別的設計模式屬于結構型模式,它是作為現有的類的一個包裝,
這種模式創建了一個裝飾類,用來包裝原有的類,并在保持類方法簽名完整性的前提下,提供了額外的功能,
我們通過下面的實體來演示裝飾器模式的用法,其中,我們將把一個形狀裝飾上不同的顏色,同時又不改變形狀類,
結構圖

裝飾器模式中的角色:
- 抽象構件(Component)角色:宣告封裝器和被封裝物件的公用介面,即給出一個抽象介面,已規范準備接收附加責任的物件,
- 具體構件(ConcreteComponent)角色:類是被封裝物件所屬的類, 它定義了基礎行為, 但裝飾類可以改變這些行為,
- 裝飾(Decorator)角色:擁有一個指向被封裝物件的參考成員變數, 該變數的型別應當被宣告為通用部件介面, 這樣它就可以參考具體的部件和裝飾, 裝飾基類會將所有操作委派給被封裝的物件,
- 具體裝飾(ConcreteDecorator)角色:定義了可動態添加到部件的額外行為, 具體裝飾類會重寫裝飾基類的方法, 并在呼叫父類方法之前或之后進行額外的行為,負責給構件物件“貼上”附加的責任,
實作
實作一個開發完成后的產品,對其進行手工功能測驗、自動化測驗、壓力測驗,將產品作為被裝飾者,也就是構件,各種測驗作為裝飾者,計算附加不同的測驗花費的總時間,
實作思路:
- 定義一個產品抽象類,
- 實作具體的產品,具體的產品繼承產品抽象類,
- 定義一個測驗型別的抽象裝飾類,繼承產品抽象類,
- 實作不同型別的測驗,繼承測驗型別的抽象裝飾類,
- 使用時實體化一個產品,然后對產品進行附件不同的測驗型別,
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; } } }
運行后結果:
未執行ProductA測驗時總共花費時間0 執行ProductA+手工測驗總共花費時間200 ===================================== 執行ProductB+手工測驗+壓力測驗+自動化測驗總共花費時間500
應用場景
- 在無需修改代碼的情況下即可使用物件, 且希望在運行時為物件新增額外的行為時可以使用裝飾模式,因為裝飾能將業務邏輯組織為層次結構,可為各層創建一個裝飾, 在運行時將各種不同邏輯組合成物件, 由于這些物件都遵循通用介面, 客戶端代碼能以相同的方式使用這些物件,
- 如果用繼承來擴展物件行為的方案難以實作或者根本不可行,可以使用裝飾模式,
優缺點
優點
- 無需創建新子類即可擴展物件的行為,
- 可以在運行時添加或洗掉物件的功能,
- 可以用多個裝飾封裝物件來組合幾種行為,
- 裝飾類和被裝飾類可以獨立發展,不會相互耦合,
- 單一職責原則, 可以將實作了許多不同行為的一個大類拆分為多個較小的類,
缺點
- 在封裝器堆疊中洗掉特定封裝器比較困難,
- 實作行為不受裝飾堆疊順序影響的裝飾比較困難,
- 各層的初始化配置代碼看上去可能會很糟糕,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/228923.html
標籤:設計模式
