Decoractor Pattern

装饰器模式是一个很著名的设计模式,经常被用于有切面需求的场景,比如插入日志、统计等的逻辑。装饰器模式的作用是能为已经存在的对象添加额外的功能。

设计模式之装饰器模式

装饰器模式的作用

  • 在不改变现有对象结构的基础上为其添加新的功能,且并不通过继承而是通过对象之间的关联来实现,使其更加灵活。
  • 相比于继承来增加新功能(固定的),装饰器模式可以动态的增加新功能,并且功能之间可以进行组合来产生更多的功能,避免了增加功能时对对象的修改
  • 这是一种实现AOP(面向切面编程)的方法,即将主业务逻辑之外的aspect抽离出来,降低代码的耦合度。

实现装饰器模式的关键

装饰器模式无非是要调用一堆方法,但是又不能直接在类中增加逻辑(开闭原则),也不通过继承来实现,而是交给装饰器来实现。那么问题的关键就在于保存这一堆方法,并且使他们之间的顺序可以动态的调整。装饰器模式是通过字段对方法进行缓存(实际上是缓存一个对象,然后调用对象的方法),再通过迭代调用将各个方法以一定的顺序连接起来

装饰器实现步骤

  1. 建立一个继承自对象抽象类的装饰器基类,其中包括缓存字段,构造函数和方法调用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class BaseStudentDecorator : AbstractStudent
    {
    private AbstractStudent _student = null;//用于缓存一个对象
    public BaseStudentDecorator(AbstractStudent student)
    {
    this._student = student;
    }
    public override void Finish()
    {
    this._student.Finish();
    }
    }
  1. 依照单一职责原则创建实现具体功能的装饰器,在其中调用父类的构造函数并复写父类的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public StudentLearningDecoractor(AbstractStudent student) : base(student)
    {

    }
    public override void Finish()
    {
    {
    //你要实现的逻辑
    }
    base.Finish();

    }
    //创建多个,根据要实现的逻辑
  1. 对装饰器要实现的功能进行注册并调用

    1
    2
    3
    4
    5
    6
    student = new StudentPreviewDecoractor(student);
    student = new StudentLearningDecoractor(student);//添加了一个功能
    student = new StudentReviewDecorator(student);//添加了一个功能
    student = new StudentPlayDecoractor(student);//添加了一个功能
    student = new StudentSleeepDecorator(student);//添加了一个功能
    student.Finish();//调用

抽象类和具体类

public abstract class AbstractStudent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public abstract class AbstractStudent
{
public int Id { get; set; }
public string Name { get; set; }
public abstract void Finish();
}
public class StudentCommon:AbstractStudent
{
public override void Finish()
{
Console.WriteLine($"{base.Name} finished learning");
}
public class StudentPreview: StudentCommon
{
public override void Finish()
{
base.Finish();
Console.WriteLine("先进行复习");
}
}
}

运行结果:

mark

深入分析

  • [ ] 当我们在注册方法时,student(不同类型)对象会被保存在基类中

  • [ ] 当我们最后调用finish方法时,会先调用StudentSleeepDecorator的finish方法,finish方法又会调用基类的finish方法,而基类的finish方法会调用之前缓存的student的finish方法…..,从而形成了一系列的调用,相当于一个链表结构。

  • [ ] 在我们实现具体的装饰器时,==base.Finish()和你要实现的逻辑的顺序直接影响到这些逻辑调用的顺序==。当你要实现的逻辑放在前面时,相当于堆栈,遵从先进后出的原则,因此先注册的方法会最后执行(1);当base.Finish()放在前面时,相当于队列,遵从先进先出,因此先注册的方法最先执行(2)。

  • [ ] 所以当我们要实现在主逻辑(这里为Console.WriteLine($”{base.Name} finished learning”),即已有对象的方法)前增加若干方法,在主逻辑后也增加若干方法时:

    • 在编写装饰器时时,对于主逻辑之前要执行的方法应按上述(1)来编写装饰器;而对于主逻辑之后要执行的方法应按上述(2)来编写装饰器。

    • 在注册时,对于主逻辑之前要执行的方法要逆序注册;对于主逻辑之后要执行的方法要顺序注册

tips

观察者模式较其他设计模式来说要更难以理解,相要真正理解还是得自己敲一下代码,断点运行一下。

文章目录
  1. 1. 设计模式之装饰器模式
    1. 1.1. 装饰器模式的作用
    2. 1.2. 实现装饰器模式的关键
    3. 1.3. 装饰器实现步骤
    4. 1.4. 深入分析
      1. 1.4.1. tips