[设计模式系列] 装饰模式

184 阅读5分钟

“这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战

装饰器模式介绍

俄罗斯套娃

装饰器的核心就是再不改原有类的基础上给类新增功能。可以避免继承导致的子类过多,可避免AOP带来的复杂性。

装饰器主要解决的时直接继承下因功能的不断横向扩展导致子类膨胀的问题,而是用装饰器模式后就会比直接继承显得更加灵活同时这样也就不再需要考虑子类的维护。

在实现的过程中,具体实现只关心扩展部分的功能,同时不会影响原有类的核心服务,也不会因为使用继承方式而导致的多余子类,增加了整体的灵活性。

装饰器模式满足单一职责原则,可以在自己的装饰类中完成功能逻辑的扩展,而不影响主类,同时可以按需在运行时添加和删除这部分逻辑。另外装饰器模式与继承父类重写方法,在某些时候需要按需选择,并不一定某一个就是最好。

装饰器实现的重点是对抽象类继承接口方法的使用,同时设定被继承的接口可以通过构造函数传递其实现类,由此增加扩展性并重写方法里可以实现此部分父类实现的功能。

封装器是一个能与其他目标对象连接的对象。封装器包含与目标对象相同的一系列方法,它会将所有接收到的请求委派给目标对象,但是封装器可以在将请求委派给目标前后对其进行处理,所以可能会改变最终结果。

装饰模式由于目标对象和装饰器遵循同一接口,因此你可用装饰来对对象进行无限次的封装,结果对象将获得所有封装器叠加而来的行为。

装饰器模式的抽象点

  • 抽象构件角色-定义抽象接口

  • 具体构件角色-实现抽象接口,可以是一组

  • 装饰角色-定义抽象类并继承接口中的方法,保证一致性

  • 具体装饰角色-扩展装饰具体的实现逻辑


继承可能引发的几个严重问题:

1、继承是静态的。你无法在运行时更改已有对象的行为,只能使用由不同子类创建的对象来替代当前的整个对象。

2、子类只能有一个父类。大部分编程语言不支持多继承。

装饰器结构

  • 部件 声明封装器和被封装对象的公用接口。
  • 具体部件 类是被封装对象所属的类,它定义了基础行为,但装饰类可以改变这些行为。
  • 基础装饰 类拥有一个指向被封装对象的引用成员变量。该变量的类型应当被声明为通用部件接口,这样它就可以引用具体的部件和装饰。装饰基类会将所有操作委派给被封装的对象。
  • 具体装饰类 定义了可动态添加到部件的额外行为。具体装饰类会重写装饰基类的方法,并在调用父类方法之前或之后进行额外的行为。
  • 客户端 可以使用多层装饰来封装部件,只要它能使用通用接口与所有对象交互即可。

适用场景

1、在无需修改代码的情况下即可使用对象,且希望在运行时为对象新增额外的行为,可以使用装饰模式。

装饰能将业务逻辑组织为层次结构,你可为各层创建一个装饰,在运行时将各种不同逻辑组成对象,由于这些对象都遵循通用接口,客户端代码能以相同的方式使用这些对象。

2、当某些业务无法使用继承来扩展对象行为时,可以使用装饰模式。

装饰器模式优缺点

优点:

  • 你需要创建新子类即可扩展对象的行为。

  • 可在运行时添加或删除对象的功能。

  • 可以用多个装饰封装对象来组合几种行为。

  • 满足单一职责原则。

缺点:

  • 在封装器栈中删除特定封装器比较困难

  • 实现行为不受装饰栈顺序影响的装饰比较困难

  • 刚开始各层的初始化配置代码比较糟糕

Demo

    /// <summary>
    /// 组件
    /// </summary>
    public abstract class Component
    {
        public abstract string Operation();
    }
}
    /// <summary>
    /// 抽象的装饰者
    /// </summary>
    class ConcreteComponent:Component
    {
        public override string Operation()
        {
            return "ConcreteComponent";
        }
    }
    abstract class Decorator:Component
    {
        protected Component _component;
        public Decorator(Component component)
        {
            this._component = component;
        }

        public void SetComponent(Component component) 
        {
            this._component = component;
        }

        public override string Operation()
        {
            if (this._component!=null)
            {
                return this._component.Operation();
            }
            else
            {
                return string.Empty;
            }
        }
    }
    class ConcreteDecoratorA : Decorator
    {
        public ConcreteDecoratorA(Component comp):base(comp)
        {

        }

        public override string Operation()
        {
            return "ConcreteDecoratorA " + base.Operation();
        }
    }
    
    class ConcreteDecoratorB:Decorator
    {
        public ConcreteDecoratorB(Component comp)
            : base(comp)
        {

        }

        public override string Operation()
        {
            return "ConcreteDecoratorA " + base.Operation();
        }
    }    
    public class Client
    {
        public void ClientCode(Component component) 
        {
            Console.WriteLine("Result:"+component.Operation());
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Client client = new Client();

            var temp = new ConcreteComponent();
            Console.WriteLine("Start------");
            Console.WriteLine();

            ConcreteDecoratorA d1 = new ConcreteDecoratorA(temp);
            ConcreteDecoratorB d2 = new ConcreteDecoratorB(d1);
            Console.WriteLine("Start Music");
            client.ClientCode(d2);
            Console.ReadKey();
        }
    }

计算结果

其实可以看出装饰器模式和组合模式还是有异曲同工之处的,装饰器模式是对既有类中方法的可重写,也就是可以改变其方法的结构,而组合则是不能改变其既有类中的方法,只能将其组合成想要实现的样子。

小寄语

人生短暂,我不想去追求自己看不见的,我只想抓住我能看的见的。

我是阿辉,感谢您的阅读,如果对你有帮助,麻烦点赞、转发 谢谢。