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),既沒有更改寫好的模塊,又添加了一些需要的輔助措施,注意是輔助措施,如果是核心玩法之類的改動,筆者還是建議不要用裝飾器模式了。