0、主要参考
(1)讲的较好:https://blog.csdn.net/mzl87/article/details/127878302
(2)https://www.cnblogs.com/for-easy-fast/archive/2023/06/05/17458083.html
(3)也还行:https://www2014.aspxhtml.com/post-20244/
1、概念
1.1问题来源
(1)子类复子类,子类何其多。
图片来源https://www.bilibili.com/video/BV1Jz4y1d7TX?p=10
1.2装饰器模式的作用
装饰器模式旨在动态地向现有对象添加附加功能。装饰器为扩展功能提供了子类化的替代方法。虽然可以通过对象类的子类化来为整个对象类添加功能,但装饰器模式旨在仅向单个对象添加功能,而使该类的其他对象保持不变。
- (1)有时有人评论说,通过在不修改原始类型的情况下增强现有类型,该模式遵循开闭原则。
- (2)非常有趣的是,该模式通过违背OO建议的错误方式来实现其目标。
1.3组合与继承的选择?
面向对象设计的典型问题:
(1)对于给定的类/对象A,在创建要重用类/对象A的新类/对象B时,如何以及基于什么标准在组合和继承之间进行选择?通常给出的方案是:如果您打算重用现有类A的Public接口,请使用继承。如果您打算仅重用现有类A的功能,请选择组合。
然而装饰器模式正好相反:
它计划重用现有类的Public接口,但使用组合。这个决定对Public接口扩展的问题有一些影响,我们稍后会看到。
2 装饰器使用的动机和意图
2.1动机
子类复子类,子类何其多,功能扩展越来越复杂。上述描述的问题根源在于我们“过度地使用了继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀(多继承)。
如何使“对象功能的扩展”能够根据需要来动态地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响降为最低?
2.2意图
动态地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类更灵活。(《设计模式》GoF)
2.3 用处
- 在软件开发过程中,经常会遇到需要给一个对象添加一些额外的行为的情况。如果直接修改这个对象的代码,可能会引起其他代码的不稳定性,而且会导致代码的可维护性变差。此时,装饰器模式就可以很好地解决这个问题。
- 使用装饰器模式,你可以在运行时动态地给一个对象添加一些额外的职责,而不需要修改这个对象的代码。这样,你就可以保持原有代码的稳定性和可维护性,同时还能够满足新的需求。
3 装饰器的实现
3.1装饰器模式的组成
装饰器模式的实现需要以下几个步骤:
- (1)定义一个接口(或者抽象组件),它包含了被装饰对象和装饰器对象需要实现的方法。
- (2)实现一个具体的类,它实现了这个接口,并且包含了一些基本的行为。
- (3)实现一个装饰器类(这是一个抽象类),它也实现了这个接口,并且包含了一个指向被装饰对象的引用。
- (4)在装饰器类中,实现需要添加的额外行为,并且在调用被装饰对象的方法时,也调用相应的方法。
- (5)在客户端代码中,创建一个具体的对象,并且用装饰器类动态地给它添加一些额外的行为。
3.2装饰器的结构图
(1)
(2)
3.3测试例子
参考了https://www.cnblogs.com/for-easy-fast/archive/2023/06/05/17458083.html
(1)定义一个接口
比如定义个形状的类
// Component 接口
public interface IShape
{
void Draw();
}
(2)定义一个具体的类,实现了上面的接口
比如定义一个矩形的类的实现
// ConcreteComponent 类
public class Rectangle : IShape
{
public void Draw()
{
Console.WriteLine("Drawing a rectangle.");
}
}
(3)定义装饰类,实现了上面的接口
注意:
- 继承了接口,
- 类里面有protected属性的接口字段
- 使用虚函数virtual实现接口中的方法。(如果接口使用抽象类定义,则可以重写,并使用相同的方法实现)
// Decorator 类
public abstract class ShapeDecorator : IShape
{
protected IShape decoratedShape;
public ShapeDecorator(IShape decoratedShape)
{
this.decoratedShape = decoratedShape;
}
public virtual void Draw()
{
decoratedShape.Draw();
}
}
(4)定义2个具体的类,进行各自的功能扩展
//(4) ConcreteDecorator 类1
public class RedShapeDecorator : ShapeDecorator
{
public RedShapeDecorator(IShape decoratedShape) : base(decoratedShape)
{
}
public override void Draw()
{
decoratedShape.Draw();
//具体功能扩展测试代码如下
Console.WriteLine("Border Color: Red_1");
}
}
//(5) ConcreteDecorator 类2
public class BlueShapeDecorator : ShapeDecorator
{
public BlueShapeDecorator(IShape decoratedShape) : base(decoratedShape)
{
}
public override void Draw()
{
decoratedShape.Draw();
//具体功能扩展测试代码如下
Console.WriteLine("Border Color: blue2_1");
}
}
(5)客户端的测试代码
// 客户端代码
public class Client
{
public void run()
{
// (1)创建一个具体的对象
IShape rectangle = new Rectangle();
// (2)用装饰器类动态地给它添加一些额外的行为
IShape redRectangle = new RedShapeDecorator(rectangle);
// (3)原始具体类的输出
rectangle.Draw();
Console.WriteLine("-----------------------------");
// (4)用具体类装饰一下输出
redRectangle.Draw();
Console.WriteLine("-----------------------------");
// (5)注意啦,对上面的装饰器结果接着进行装饰!!!
IShape blueShapeDecorator = new BlueShapeDecorator(redRectangle);
blueShapeDecorator.Draw();
}
}
public class Demo
{
static void Main(string[] args)
{
Client client = new Client();
client.run();
}
}
(6)测试结果
4、相关过程和注意实现
(1)接口(抽象类)定义了规范
(2)具体类A实现了基本功能
(3)装饰器(抽象类)对接口进行了实现,并定义了protected的接口字段
(4)装饰器的具体类对接口功能进行了拓展
(5)客户端通过创建具体类A;在装饰器具体类A中,并在构造函数传入具体类A;从而拓展了具体类A的扩展功能
(6)客户端还可以创建装饰器具体类B,并在构造函数传入装饰器具体类A,接着扩展了装饰器A,从而实现了2个扩展功能
(7)装饰器可以一路装饰下去。