用心理解设计模式——装饰器模式 (Decorator Pattern)

前置文章: 设计模式的原则 

其他设计模式:用心理解设计模式专栏

设计模式相关代码已统一放至 我的 Github

  一、定义

  结构型模式之一。

  Attach additional responsibilites to an object dynamically keeping the same interface. Decorators provide a flexible alternative to subclassing for extending functionality.

  (动态地为一个对象添加一些额外的职责并保持接口不变。装饰器为扩展功能提供了一个灵活的替代子类的方法。)

二、结构解析

   装饰器模式的一般结构有四种角色: 抽象组件、具体组件、抽象装饰器、具体装饰器。

   抽象组件(Component):负责定义具体组件要被装饰的业务方法接口。

   具体组件(ConcretComponent):被装饰者,继承抽象组件,实现自己原有的方法。

   抽象装饰器(Decorator ):继承抽象组件,持有(聚合)一个抽象组件类型的成员,实际可接收一个具体组件另一个装饰器。可将继承自抽象组件的接口方法定义为 模板方法(即,约定装饰规则),然后让子类去实现具体的装饰逻辑(下面示例就是这样做的)。或者,也可给出空的或默认的装饰实现,由子类选择性的重写。最后,不管怎么做,装饰器实现的接口方法,都要最终调用持有的成员实现的该接口方法(只装饰,不完全重写)。

   具体装饰器(ConcreteDecorator):负责实现具体的装饰逻辑,发挥实际的装饰作用,派生不同的具体装饰器可以产生不同的装饰效果。

三、评价

  装饰器模式,具有如继承一样,扩展某个类职责的功能。 但它与继承不同的是,继承是通过扩展出子类特有接口的方式来扩展职责,装饰器是以增强原接口功能的方式(保持接口不变,扩展的职责被原接口调用)来扩展类的职责。

  装饰器模式在特定情况下,比继承更简洁、更灵活。

  它可以在 “只定义具体组件的各种不同的单一装饰类” 的情况下,将这些单一装饰类进行动态地排列组合产生大量不同的组合装饰效果(注意“排列”和“组合”都能产生不同)。而如果使用继承的方式,则需要将 单一装饰类 和 组合装饰类 都静态地定义出来。

  装饰器模式,可以进行排列组合的核心在于,抽象装饰器可以持有另一个装饰器。

  装饰器都是继承自抽象组件的。它实现的接口方法,都调用被装饰者的对应的接口方法。所以,这样的持有调用将让装饰方法一层层叠加起来。可以理解为是在装饰的基础上继续装饰

  用一个图来表示就很清晰了(横向可自由排列组合不同的装饰器),如下:

  装饰器在只有一层装饰时(没有组合需求时),可以增加除了原接口方法之外的其他接口方法和属性(即,不用保持接口不变),但这不是装饰器模式想要表达的核心思想,不推荐这么干。

  多层装饰时,最外层也可扩展额外的方法和属性。也不推荐这样干。

  装饰器模式的结构和 代理模式 很相似,但其设计思想则完全不同。代理模式是为了对被代理者进行访问控制,不会对被代理者的职责进行扩展。

四、实现

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Decorator
{
    //抽象组件
    public abstract class Component
    {
        public abstract void DoSth();
    }

    //具体组件
    public class ConcretComponent : Component
    {
        public override void DoSth()
        {
            Debug.Log("做X事情");
        }
    }

    //抽象装饰类
    public abstract class Decorator : Component
    {
        public Component component;
        public Decorator(Component component)
        {
            this.component = component;
        }

        //这里方便起见,将DoSth定位一个模板方法。实际可以由子类决定DoSth时,除了做原来的事,还要怎么做别的事。
        public override void DoSth()
        {
            this.component.DoSth();
            DoOtherThing();
        }

        public abstract void DoOtherThing();
    }

    //具体装饰类A
    public class ConcreteDecoratorA : Decorator
    {
        public ConcreteDecoratorA(Component component) : base(component){ }

        //增加其他事情
        public override void DoOtherThing()
        {
            Debug.Log("做A事情");
        }
    }

    //具体装饰类B
    public class ConcreteDecoratorB : Decorator
    {
        public ConcreteDecoratorB(Component component) : base(component) { }

        //增加其他事情
        public override void DoOtherThing()
        {
            Debug.Log("做B事情");
        }
    }

    //具体装饰类C
    public class ConcreteDecoratorC : Decorator
    {
        public ConcreteDecoratorC(Component component) : base(component) { }

        //增加其他事情
        public override void DoOtherThing()
        {
            Debug.Log("做C事情");
        }
    }

    public class Client
    {
        static public void Main()
        {
            //正常情况下,具体做X事。
            ConcretComponent cc = new ConcretComponent();
            cc.DoSth();

            //现在想让组件类,除了能做X事,还能做A事。
            //此时不用修改这个组件类。
            //而是将其放入具体装饰器(或称为“包装器”更贴切)中。
            ConcreteDecoratorA cdA = new ConcreteDecoratorA(cc);
            ConcreteDecoratorB cdB = new ConcreteDecoratorB(cc);
            ConcreteDecoratorB cdC = new ConcreteDecoratorB(cc);

            cdA.DoSth();    //既能干X、又能干A
            cdB.DoSth();    //既能干X、又能干B
            cdC.DoSth();    //既能干X、又能干C

            //------------------------------------------------------------------
            //除了上边那样简单的单层装饰,还能进行任意组合的装饰

            new ConcreteDecoratorB(cdA).DoSth();    //既能干X、又能干A、又能干B
            new ConcreteDecoratorA(cdB).DoSth();    //同上,顺序变化

            new ConcreteDecoratorC(cdA).DoSth();    //既能干X、又能干A、又能干C
            new ConcreteDecoratorA(cdC).DoSth();    //同上,顺序变化

            new ConcreteDecoratorC(cdB).DoSth();    //既能干X、又能干B、又能干C
            new ConcreteDecoratorB(cdC).DoSth();    //同上,顺序变化

            new ConcreteDecoratorA(new ConcreteDecoratorB(cdC)).DoSth();    //既能干X、又能干A、又能干B、又能干C
            new ConcreteDecoratorA(new ConcreteDecoratorC(cdB)).DoSth();    //同上,顺序变化
            new ConcreteDecoratorB(new ConcreteDecoratorA(cdC)).DoSth();    //同上,顺序变化
            new ConcreteDecoratorB(new ConcreteDecoratorC(cdA)).DoSth();    //同上,顺序变化
            new ConcreteDecoratorC(new ConcreteDecoratorA(cdB)).DoSth();    //同上,顺序变化
            new ConcreteDecoratorA(new ConcreteDecoratorB(cdA)).DoSth();    //同上,顺序变化

            //------------------------------------------------------------------
            //甚至可以同方法多次装饰
            new ConcreteDecoratorA(cdA).DoSth();
            new ConcreteDecoratorA(new ConcreteDecoratorA(cdA)).DoSth();
        }
    }
}

猜你喜欢

转载自blog.csdn.net/NRatel/article/details/84329030