0、主要參考
(1)講的較好:https://blog.csdn.net/mzl87/article/details/127878302
(2)https://www.cnblogs.com/for-easy-fast/archive/2023/06/05/17458083.html
(3)也還行:https://www2014.aspxhtml.com/post-20244/
1、概念
1.1問題來源
(1)子類複子類,子類何其多。
圖檔來源https://www.bilibili.com/video/BV1Jz4y1d7TX?p=10
1.2裝飾器模式的作用
裝飾器模式旨在動态地向現有對象添加附加功能。裝飾器為擴充功能提供了子類化的替代方法。雖然可以通過對象類的子類化來為整個對象類添加功能,但裝飾器模式旨在僅向單個對象添加功能,而使該類的其他對象保持不變。
- (1)有時有人評論說,通過在不修改原始類型的情況下增強現有類型,該模式遵循開閉原則。
- (2)非常有趣的是,該模式通過違背OO建議的錯誤方式來實作其目标。
1.3組合與繼承的選擇?
面向對象設計的典型問題:
(1)對于給定的類/對象A,在建立要重用類/對象A的新類/對象B時,如何以及基于什麼标準在組合和繼承之間進行選擇?通常給出的方案是:如果您打算重用現有類A的Public接口,請使用繼承。如果您打算僅重用現有類A的功能,請選擇組合。
然而裝飾器模式正好相反:
它計劃重用現有類的Public接口,但使用組合。這個決定對Public接口擴充的問題有一些影響,我們稍後會看到。
2 裝飾器使用的動機和意圖
2.1動機
子類複子類,子類何其多,功能擴充越來越複雜。上述描述的問題根源在于我們“過度地使用了繼承來擴充對象的功能”,由于繼承為類型引入的靜态特質,使得這種擴充方式缺乏靈活性;并且随着子類的增多(擴充功能的增多),各種子類的組合(擴充功能的組合)會導緻更多子類的膨脹(多繼承)。
如何使“對象功能的擴充”能夠根據需要來動态地實作?同時避免“擴充功能的增多”帶來的子類膨脹問題?進而使得任何“功能擴充變化”所導緻的影響降為最低?
2.2意圖
動态地給一個對象增加一些額外的職責。就增加功能而言,Decorator模式比生成子類更靈活。(《設計模式》GoF)
2.3 用處
- 在軟體開發過程中,經常會遇到需要給一個對象添加一些額外的行為的情況。如果直接修改這個對象的代碼,可能會引起其他代碼的不穩定性,而且會導緻代碼的可維護性變差。此時,裝飾器模式就可以很好地解決這個問題。
- 使用裝飾器模式,你可以在運作時動态地給一個對象添加一些額外的職責,而不需要修改這個對象的代碼。這樣,你就可以保持原有代碼的穩定性和可維護性,同時還能夠滿足新的需求。
3 裝飾器的實作
3.1裝飾器模式的組成
裝飾器模式的實作需要以下幾個步驟:
- (1)定義一個接口(或者抽象元件),它包含了被裝飾對象和裝飾器對象需要實作的方法。
- (2)實作一個具體的類,它實作了這個接口,并且包含了一些基本的行為。
- (3)實作一個裝飾器類(這是一個抽象類),它也實作了這個接口,并且包含了一個指向被裝飾對象的引用。
- (4)在裝飾器類中,實作需要添加的額外行為,并且在調用被裝飾對象的方法時,也調用相應的方法。
- (5)在用戶端代碼中,建立一個具體的對象,并且用裝飾器類動态地給它添加一些額外的行為。
3.2裝飾器的結構圖
(1)
(2)
3.3測試例子
參考了https://www.cnblogs.com/for-easy-fast/archive/2023/06/05/17458083.html
(1)定義一個接口
比如定義個形狀的類
// Component 接口
public interface IShape
{
void Draw();
}
(2)定義一個具體的類,實作了上面的接口
比如定義一個矩形的類的實作
// ConcreteComponent 類
public class Rectangle : IShape
{
public void Draw()
{
Console.WriteLine("Drawing a rectangle.");
}
}
(3)定義裝飾類,實作了上面的接口
注意:
- 繼承了接口,
- 類裡面有protected屬性的接口字段
- 使用虛函數virtual實作接口中的方法。(如果接口使用抽象類定義,則可以重寫,并使用相同的方法實作)
// Decorator 類
public abstract class ShapeDecorator : IShape
{
protected IShape decoratedShape;
public ShapeDecorator(IShape decoratedShape)
{
this.decoratedShape = decoratedShape;
}
public virtual void Draw()
{
decoratedShape.Draw();
}
}
(4)定義2個具體的類,進行各自的功能擴充
//(4) ConcreteDecorator 類1
public class RedShapeDecorator : ShapeDecorator
{
public RedShapeDecorator(IShape decoratedShape) : base(decoratedShape)
{
}
public override void Draw()
{
decoratedShape.Draw();
//具體功能擴充測試代碼如下
Console.WriteLine("Border Color: Red_1");
}
}
//(5) ConcreteDecorator 類2
public class BlueShapeDecorator : ShapeDecorator
{
public BlueShapeDecorator(IShape decoratedShape) : base(decoratedShape)
{
}
public override void Draw()
{
decoratedShape.Draw();
//具體功能擴充測試代碼如下
Console.WriteLine("Border Color: blue2_1");
}
}
(5)用戶端的測試代碼
// 用戶端代碼
public class Client
{
public void run()
{
// (1)建立一個具體的對象
IShape rectangle = new Rectangle();
// (2)用裝飾器類動态地給它添加一些額外的行為
IShape redRectangle = new RedShapeDecorator(rectangle);
// (3)原始具體類的輸出
rectangle.Draw();
Console.WriteLine("-----------------------------");
// (4)用具體類裝飾一下輸出
redRectangle.Draw();
Console.WriteLine("-----------------------------");
// (5)注意啦,對上面的裝飾器結果接着進行裝飾!!!
IShape blueShapeDecorator = new BlueShapeDecorator(redRectangle);
blueShapeDecorator.Draw();
}
}
public class Demo
{
static void Main(string[] args)
{
Client client = new Client();
client.run();
}
}
(6)測試結果
4、相關過程和注意實作
(1)接口(抽象類)定義了規範
(2)具體類A實作了基本功能
(3)裝飾器(抽象類)對接口進行了實作,并定義了protected的接口字段
(4)裝飾器的具體類對接口功能進行了拓展
(5)用戶端通過建立具體類A;在裝飾器具體類A中,并在構造函數傳入具體類A;進而拓展了具體類A的擴充功能
(6)用戶端還可以建立裝飾器具體類B,并在構造函數傳入裝飾器具體類A,接着擴充了裝飾器A,進而實作了2個擴充功能
(7)裝飾器可以一路裝飾下去。