前言:笔者在最开始写程序的时候经常会遇到一种情况,例如更改一个字段、或者添加一种小功能,就要把原来写过的东西几乎废弃掉,或者更改大量以前写过的代码。又或者自己写的东西时间久了再去回顾,完全找不到到时为什么这么写的头绪,如果遇到了Bug更是无法快速定位在哪里小范围出现的问题。如果你也经常遇到这种问题,就说明你现阶段非常需要学习下设计模式了。
在网上经常说的设计模式有23种,也有一些更多的设计模式,无非也是从这些设计模式中变种而来。如果让笔者来形容什么是设计模式,我认为设计模式是:一种思想,一种模式,一种套路,一种解决问题的高效策略。
有说的不正确或者不准确的地方欢迎留言指正
有什么有趣的写作技巧或者想法欢迎大家给我留言,大家的帮助是我写下去最有效的动力
圣诞树是圣诞节中必不可少的东西,为什么是圣诞树,而不是弄个什么圣诞花或者圣诞草之类的笔者没有深入研究。下面的两张圣诞树图片是笔者找到认为最漂亮的,本打算只留一张,但确实不好取舍,索性就都用了。我们今天要说的设计模式,就以圣诞树为主题展开
圣诞树一般都是以松树为核心主题,然后在最上面添加圣诞星,树上挂一些彩球、彩灯之类的装饰品,圣诞树下放一些礼物。但是网上有人说挂毛主席头像,笔者表示可能落伍了,这波操作真的看不懂!!!
对于这种结构的操作形式,在我们的设计模式中也有一个相近的形式,那就是----【装饰器模式】
首先我们的需求是这样的,先创建一个主题圣诞树,然后调用展示圣诞树函数
public class ChristmasTree
{
private readonly string core = "圣诞树";
public virtual void Show()
{
Debug.Log($"展示圣诞节的核心{core}");
}
}
然后我们有了新的需求,在展示里面添加圣诞星对应的信息
public class ChristmasTree
{
private readonly string core = "圣诞树";
public virtual void Show()
{
Debug.Log($"展示圣诞节的核心{core}");
/************************/
Debug.Log("展示圣诞星星");
/************************/
}
}
现在策划大佬感觉装饰品不够丰富,要添加圣诞彩灯、圣诞礼物或者一些其他的东西,那我们也在原有的Show函数中添加吗?如果这么做的话显然违反了我们的开闭原则,那么对于这种情况我们应该:把每个要装饰的功能放在单独的类中,并让这个类包装她所要装饰的对象,因此,当需要执行特殊行为时,客户代码就可以在运行时根据需要有选择地、按顺序地使用装饰功能包装对象了。
第一步,为ChristmasTree创建装饰器基类
public class ChristmasTreeDecorator : ChristmasTree
{
/// <summary>
/// 上一次圣诞树的状态
/// </summary>
protected ChristmasTree christmasTree = null;
public ChristmasTreeDecorator(ChristmasTree tempChristmasTree)
{
this.christmasTree = tempChristmasTree;
}
public override void Show()
{
//上一次圣诞树的状态
this.christmasTree.Show();
}
}
有了装饰器的基类后,我们可以在这个基础上创建圣诞星装饰器、彩灯装饰器、礼物装饰器等等~~~
public class ChristmasTreeDecorator_Start : ChristmasTreeDecorator
{
public ChristmasTreeDecorator_Start(ChristmasTree tempChristmasTree) : base(tempChristmasTree) { }
/// <summary>
/// 在上一次的状态基础上进行装饰
/// </summary>
public override void Show()
{
Debug.Log("展示在最上面装饰了一个圣诞星");
//上一次圣诞树的状态
base.christmasTree.Show();
}
}
public class ChristmasTreeDecorator_Lights : ChristmasTreeDecorator
{
public ChristmasTreeDecorator_Lights(ChristmasTree tempChristmasTree) : base(tempChristmasTree) { }
/// <summary>
/// 在上一次的状态基础上进行装饰
/// </summary>
public override void Show()
{
//上一次圣诞树的状态
base.christmasTree.Show();
Debug.Log("展示在最下面装饰了一个圣诞彩灯");
}
}
public class ChristmasTreeDecorator_Gift : ChristmasTreeDecorator
{
public ChristmasTreeDecorator_Gift(ChristmasTree tempChristmasTree) : base(tempChristmasTree) { }
/// <summary>
/// 在上一次的状态基础上进行装饰
/// </summary>
public override void Show()
{
//上一次圣诞树的状态
base.christmasTree.Show();
Debug.Log("展示在最下面放了一些礼物");
}
}
然后我们的运行代码可以这么用
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();
}
打印信息
这样我们可以有效地把类的核心职责和装饰功能区分开了,而且可以去除相关类中重复的装饰逻辑
还没有完,对于执行代码我们可以继续重构优化
既然所有的装饰器都是继承自装饰器基类,我们可以这么写
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();
}
装饰器基类又是继承自ChristmasTree,又可以这么写
public void Function_Three()
{
ChristmasTree christmasTree = new ChristmasTree();
ChristmasTree tree_Lights = new ChristmasTreeDecorator_Lights(christmasTree);
ChristmasTree tree_Gift = new ChristmasTreeDecorator_Gift(tree_Lights);
ChristmasTree tree_Start = new ChristmasTreeDecorator_Start(tree_Gift);
tree_Start.Show();
}
既然所有的声明都是ChristmasTree ,我们最后这样写
public void Function_Four()
{
ChristmasTree christmasTree = new ChristmasTree();
christmasTree = new ChristmasTreeDecorator_Lights(christmasTree);
christmasTree = new ChristmasTreeDecorator_Gift(christmasTree);
christmasTree = new ChristmasTreeDecorator_Start(christmasTree);
christmasTree.Show();
}
装饰器模式(Decorator),动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
这样我们的装饰器就大功告成了,其实把装饰器模式这种思想发扬光大的另一种编程方式就是面向切面编程(AOP),感兴趣的同学可以去了解一下~
在开发中我们一般在什么地方用到呢?
例如我们已经写好一个相应的战斗模块,但是在进入之前或者进入之后添加一些,输出日志、数据监测、数据收集埋点触发之类的功能,其实这些都可以用这个装饰器模式~既没有更改写好的模块,又添加了一些需要的辅助措施,注意是辅助措施,如果是核心玩法之类的改动,笔者还是建议不要用装饰器模式了。