装饰器模式
总述
装饰器模式,Decorator Pattern,用于动态地为某个对象添加属性,在不改变原始对象的结构的前提下。
- 被修饰对象
- 装饰器
常见例子 —— 普通咖啡,可以选择加糖、或者加奶(这里糖和奶就可以是装饰器)。某天遇到了一个神奇的用户要撒胡椒,就可以把胡椒封装成一个装饰器,这样就可以不用改变普通咖啡这个对象而实现动态增添属性。
实践
下面提供一个 C# 的实现一个可以往普通的形状上添加修饰的效果
首先创建一个抽象的接口,做一个形状的 “ 契约 ”
C#
public interface IShape
{
public void Draw();
}
接着定义两个具体的形状,矩形和圆形(这边随便写的
C#
public class Rectangle : IShape
{
private int _edges = 4;
public string Name = "Rectangle";
public void Draw()
{
Console.WriteLine(Name, _edges);
}
}
public class Circle : IShape
{
public void Draw()
{
Console.WriteLine("Circle, Perfect!");
}
}
接着定义装饰器的抽象
C#
public abstract class DecoratorBase(IShape shape) : IShape
{
// 需要装饰的形状, 由构造函数注入
protected IShape _shape => shape;
public virtual void Draw()
{
_shape.Draw();
}
}
然后实现两个具体的装饰器
C#
public class StrokeDecorator(IShape shape) : DecoratorBase(shape)
{
public float strokeWidth = 1.0f;
public override void Draw()
{
base.Draw();
Console.WriteLine("With Stroke {0}", strokeWidth);
}
}
public class DropShadowDecorator(IShape shape) : DecoratorBase(shape)
{
public float distance = 1.0f;
[Range(0, 1)] public float transparent = 0.5f;
public override void Draw()
{
base.Draw();
Console.WriteLine($"With DropShadow: Distance {distance}, Transparent {transparent}");
}
}
调用 ——
C#
public class DecoratorTest : ITest
{
public void RunTest()
{
// 一个普通的矩形
Console.WriteLine("Rectangle");
IShape rectangle = new Rectangle();
rectangle.Draw();
Console.WriteLine("recWithStroke");
IShape recWithStroke = new StrokeDecorator(rectangle);
recWithStroke.Draw();
Console.WriteLine("recWithStrokeAndDropShadow:");
IShape recWithStrokeAndDropShadow = new DropShadowDecorator(recWithStroke);
recWithStrokeAndDropShadow.Draw();
}
}
首先创建了一个普通的矩形,然后先加上一个描边的特效,最后再加上一个投影的特效,最后就有了一个有描边和投影的矩形,非常的好使。一个非常常见的AE背景💦
一些思考
为什么装饰器也要继承被修饰对象的接口?——
- 业务层只需要面向被修饰对象的接口进行编程,不需要额外的成本,即无需改变调用方式
- 装饰链可任意层级叠加
- 符合里氏替换原则
写在最后
优点 | 缺点 |
---|---|
符合开闭原则,扩展功能无需修改现有代码 | 装饰链过长时,会增加调试和维护难度 |
可以动态组合多个职责,避免子类爆炸 | 每增加一个装饰器就需要写一个类,类数量增加 |
装饰器和被装饰者对客户端透明 | 多层装饰可能造成性能损耗 |