2.3設(shè)計(jì)模式之裝飾器模式(Decorator)

2.3.1 模式意圖:

不需要額外創(chuàng)建子類的情況下,動態(tài)的為對象添加功能,并且不會更改類對應(yīng)的結(jié)構(gòu)。

2.3.2 模式概念:

它屬于結(jié)構(gòu)型模式。動態(tài)地給一個(gè)對象添加一些額外的職責(zé),就增加功能來說,裝飾模式比生成子類更為靈活。

2.3.3 模式元素:

  • 構(gòu)件抽象(IComponent)
  • 構(gòu)件細(xì)節(jié)(ChristmasTree)
  • 裝飾類抽象(ChristmasTreeDecorator)
  • 裝飾類細(xì)節(jié)(ChristmasTreeDecorator_Start、ChristmasTreeDecorator_Lights等)

2.3.4 代碼示例:

【前期準(zhǔn)備】創(chuàng)建一個(gè)主題圣誕樹(核心),調(diào)用展示圣誕樹函數(shù)

public interface IComponent
{
    void Show();
}
public class ChristmasTree : IComponent
{
    private readonly string core = "圣誕樹";
    public virtual void Show()
    {
        Debug.Log($"展示圣誕節(jié)的核心{core}");
    }
}

【前期準(zhǔn)備】有了新的需求,在Show函數(shù)里添加圣誕星對應(yīng)的信息

public class ChristmasTree : IComponent
{
    private readonly string core = "圣誕樹";
    public virtual void Show()
    {
        Debug.Log($"展示圣誕節(jié)的核心{core}");
        /************************/
        Debug.Log("展示圣誕星星");
        /************************/
    }
}

要添加圣誕彩燈、圣誕禮物或者一些其他的東西,那我們也在原有的Show函數(shù)中添加嗎?
如果這么做的話顯然違反了我們的開閉原則,那么對于這種情況我們應(yīng)該:把每個(gè)要裝飾的功能放在單獨(dú)的類中,并讓這個(gè)類包裝它所要裝飾的對象,因此,當(dāng)需要執(zhí)行特殊行為時(shí),客戶代碼就可以在運(yùn)行時(shí)根據(jù)需要有選擇、有順序的使用裝飾功能包裝對象了。

A.為ChristmasTree創(chuàng)建裝飾器基類

public class ChristmasTreeDecorator : IComponent
{
    /// <summary>
    /// 上一次圣誕樹的狀態(tài)
    /// </summary>
    protected IComponent christmasTree = null;

    public ChristmasTreeDecorator(IComponent tempChristmasTree)
    {
        this.christmasTree = tempChristmasTree;
    }

    public virtual void Show()
    {
        //上一次圣誕樹的狀態(tài)
        this.christmasTree.Show();
    }
}

B.有了裝飾器的基類后,我們可以在這個(gè)基礎(chǔ)上創(chuàng)建圣誕星裝飾器、彩燈裝飾器、禮物裝飾器等.

public class ChristmasTreeDecorator_Start : ChristmasTreeDecorator
{
    public ChristmasTreeDecorator_Start(IComponent tempChristmasTree) : base(tempChristmasTree) { }
    /// <summary>
    /// 在上一次的狀態(tài)基礎(chǔ)上進(jìn)行裝飾
    /// </summary>
    public override void Show()
    {
        Debug.Log("展示在最上面裝飾了一個(gè)圣誕星");
        //上一次圣誕樹的狀態(tài)
        base.christmasTree.Show();
    }
}
public class ChristmasTreeDecorator_Lights : ChristmasTreeDecorator
{
    public ChristmasTreeDecorator_Lights(IComponent tempChristmasTree) : base(tempChristmasTree) { }
    /// <summary>
    /// 在上一次的狀態(tài)基礎(chǔ)上進(jìn)行裝飾
    /// </summary>
    public override void Show()
    {
        //上一次圣誕樹的狀態(tài)
        base.christmasTree.Show();
        Debug.Log("展示在最下面裝飾了一個(gè)圣誕彩燈");
    }
}

public class ChristmasTreeDecorator_Gift : ChristmasTreeDecorator
{
    public ChristmasTreeDecorator_Gift(IComponent tempChristmasTree) : base(tempChristmasTree) { }
    /// <summary>
    /// 在上一次的狀態(tài)基礎(chǔ)上進(jìn)行裝飾
    /// </summary>
    public override void Show()
    {
        //上一次圣誕樹的狀態(tài)
        base.christmasTree.Show();
        Debug.Log("展示在最下面放了一些禮物");
    }
}

C.然后我們的運(yùn)行代碼可以這么用

    public void Function_One()
    {
        ChristmasTree christmasTree = new ChristmasTree();

        ChristmasTreeDecorator_Lights tree_Lights = new ChristmasTreeDecorator_Lights(christmasTree);

        ChristmasTreeDecorator_Gift tree_Gift = new ChristmasTreeDecorator_Gift(tree_Lights);

        ChristmasTreeDecorator_Start tree_Start = new ChristmasTreeDecorator_Start(tree_Gift);

        tree_Start.Show();
    }

打印信息

這樣我們可以有效地把類的核心職責(zé)和裝飾功能區(qū)分開了,而且可以去除相關(guān)類中重復(fù)的裝飾邏輯

對于執(zhí)行代碼我們可以繼續(xù)重構(gòu)優(yōu)化

D.既然所有的裝飾器都是繼承自裝飾器基類,我們可以這么寫

    public void Function_Two()
    {
        ChristmasTree christmasTree = new ChristmasTree();

        ChristmasTreeDecorator tree_Lights = new ChristmasTreeDecorator_Lights(christmasTree);

        ChristmasTreeDecorator tree_Gift = new ChristmasTreeDecorator_Gift(tree_Lights);

        ChristmasTreeDecorator tree_Start = new ChristmasTreeDecorator_Start(tree_Gift);

        tree_Start.Show();
    }

E.裝飾器基類又是繼承自IComponent,又可以這么寫

    public void Function_Three()
    {
        ChristmasTree christmasTree = new ChristmasTree();

        IComponent tree_Lights = new ChristmasTreeDecorator_Lights(christmasTree);

        IComponent tree_Gift = new ChristmasTreeDecorator_Gift(tree_Lights);

        IComponent tree_Start = new ChristmasTreeDecorator_Start(tree_Gift);

        tree_Start.Show();
    }

F.所有的聲明都是IComponent ,最后這樣寫

    public void Function_Four()
    {
        IComponent christmasTree = new ChristmasTree();

        christmasTree = new ChristmasTreeDecorator_Lights(christmasTree);

        christmasTree = new ChristmasTreeDecorator_Gift(christmasTree);

        christmasTree = new ChristmasTreeDecorator_Start(christmasTree);

        christmasTree.Show();
    }

2.3.5 寫法對比:

2.3.6 模式分析:

裝飾類和被裝飾類彼此獨(dú)立,互不干擾,又不失去彼此間的關(guān)聯(lián)。裝飾模式是繼承的一個(gè)替代模式,單耦合性更低。

不足之處
隨著多層裝飾的深度增加,出錯(cuò)幾率增大,排錯(cuò)也變的復(fù)雜,代碼量增多。

2.3.7 應(yīng)用場景:

例如我們已經(jīng)寫好一個(gè)相應(yīng)的戰(zhàn)斗模塊,但是在進(jìn)入之前或者進(jìn)入之后添加一些,輸出日志、數(shù)據(jù)監(jiān)測、數(shù)據(jù)收集埋點(diǎn)觸發(fā)之類的功能,其實(shí)這些都可以用這個(gè)裝飾器模式

2.3.8 小結(jié):

把裝飾器模式這種思想發(fā)揚(yáng)光大的另一種編程方式就是面向切面編程(AOP)既沒有更改寫好的模塊,又添加了一些需要的輔助措施,注意是輔助措施,如果是核心玩法之類的改動,筆者還是建議不要用裝飾器模式了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容