场景
在很多情况下我们需要为对象来扩展更多的功能,容易想到的做法是通过继承,在父类已有功能的基础上为子类增加更多的功能。但是如果我们需要很多功能扩展就会不可避免的增加更多的子类,很容易引起子类数量的集聚膨胀。
事实上,假若采用继承我们应当充分的考虑新增的子类中的一大部分是不是也具有相同的子功能,如果是这样,那么这些相同的子功能是否可以再提炼一下成为可复用的部分。
组合优于继承(继承有陷阱它使子类数量急剧增长)
我们应该思考新增对象职责时采用继承还是组合。继承有明显的静态特征一旦继承则依赖关系相对被紧密绑定,扩展变的困难,而组合关系则是动态的松耦合的,可以留下很好的扩展余地。
实现思路
我们希望提炼出一个为指定类进行新功能扩展的稳定骨架。为了避免继承的强依赖关系,我们选择采用组合,组合惯用的方法就是在构造函数中注入依赖对象。这样的话骨架和依赖的对象关系演变为我包含你,包含了你就可以为你任意扩展新功能;同时骨架为你扩展完新的功能后最好不要演变成新的类,而应该还是你拥有了新功能的你本身。
如此就有了以下实现:
代码实现
internal class 人物类
{
protected readonly string Name;
public 人物类()
{
}
public 人物类(string name)
{
Name = name;
}
public virtual void Show()
{
Console.WriteLine($"装扮的{Name}");
}
}
/// <summary>
/// 装饰类是关键,这里精妙的声明这样的两层关系:
/// 1.装饰类同时也是一个人物类的实现
/// --即我是你,这意味着我可以拿来当你用
/// 2.同时装饰类又组合了一个人物类的实例做为它自己的一个字段成员
/// --即我包含你也可以包含别的扩展,因为我可以为你任意扩充新的功能实现。
/// </summary>
internal class 装饰类 : 人物类
{
protected 人物类 Person;
public void Decorate(人物类 person)
{
Person = person;
}
public override void Show()
{
Person?.Show();
}
}
internal class 上衣 : 装饰类
{
public override void Show()
{
Console.WriteLine("上衣");
base.Show();
}
}
internal class 帽子 : 装饰类
{
public override void Show()
{
Console.WriteLine("帽子");
base.Show();
}
}
高层调用时的代码示例:
public static void Main(string[] args)
{
var 我的人物实例 = new 人物类("秦王");
Console.WriteLine("\n第一次装扮:\n");
var 帽子实例 = new 帽子();
帽子实例.Decorate(我的人物实例);
帽子实例.Show();
Console.WriteLine("\n第二次装扮:\n");
var 上衣实例 = new 上衣();
上衣实例.Decorate(帽子实例);
上衣实例.Show();
Console.ReadLine();
}
装饰器模式的GOF官方定义
使用组合的方式动态的给对象增加额外的职责,就增加功能而言,Decorator模式(组合)比生成子类(继承)更加灵活有效。因为它右以显著减少创建子类的数量并能消除重复代码。
类图
图片引用自李建忠老师的视频教学《C++设计模式》