• 设计模式(9) 装饰模式


    • 装饰模式
    • 装饰模式的特点
    • 动态撤销功能

    装饰模式可以动态向一个现有的对象添加新的功能,同时又不改变其结构。就增加功能来说,使用继承的方式生成子类也可以达到目的,但随着扩展功能的不断增加,子类的数量会快速膨胀,而装饰模式提供了一种更加灵活的方案。

    装饰模式

    GOF对装饰模式的描述为:
    Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
    — Design Patterns : Elements of Reusable Object-Oriented Software

    UML类图:
    装饰模式 UML类图

    IComponent接口定义了现有的功能,ConcreteComponent是它的具体实现类。
    为了给IComponent扩展功能,引入了IDecorator接口,它继承了IComponent接口,ConcreteDecorator是扩展功能的具体实现。

    为了更形象地理解这一模式,模拟实现一个文字处理软件功能,最初软件只具备单纯的文字输入、显示功能,后来扩展了更高级的功能,比如字体可以加粗、文字颜色可以调整、可以有不同的字号等等。

    最初的功能

    public interface IText
    {
        string Content { get; }
    }
    
    public class TextObject : IText
    {
        public string Content { get { return "hello"; } }
    }
    

    使用装饰模式进行扩展

    public interface IDecorator : IText { }
    
    public abstract class DecoratorBase : IDecorator
    {
        protected IText target;
    
        public abstract string Content { get; }
    
        public DecoratorBase(IText target)
        {
            this.target = target;
        }
    }
    
    //字体加粗
    public class BoldDecorator : DecoratorBase
    {
        public BoldDecorator(IText target) : base(target) { }
    
        public override string Content => ChangeToBoldFont(target.Content);
    
        public string ChangeToBoldFont(string content)
        {
            return $"<b>{content}</b>";
        }
    }
    
    //字体颜色
    public class ColorDecorator : DecoratorBase
    {
        public ColorDecorator(IText target) : base(target) { }
    
        public override string Content => AddColorTag(target.Content);
    
        public string AddColorTag(string content)
        {
            return $"<color>{content}</color>";
        }
    }
    

    测试代码:

    static void Main(string[] args)
    {
        IText text = new TextObject();
        IDecorator text = new BoldDecorator(text);
        text = new ColorDecorator(text);
        Console.WriteLine(text.Content);
        //<color><b>hello</b></color>
    }
    

    装饰模式是设计模式中实现技巧性非常明显的一个模式,它的声明要实现IComponent定义的方法,但同时又会保留一个IComponent的成员,IComponent接口方法的实现其实是通过自己保存的那个IComponent成员完成的,自己在这个基础上增加一些额外的处理。

    装饰模式的特点

    适用场景

    • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。毕竟客户程序依赖的仅仅是IComponent接口,至于这个接口被做过什么装饰只有实施装饰的对象才知道,而客户程序只负责根据IComponent的方法调用。
    • 屏蔽某些职责,也就是在套用某个装饰类型的时候,并不增加新的特征,而只把既有方法屏蔽。
    • 避免出现为了适应变化而子类膨胀的情况。

    缺点

    装饰模式虽然提供了比继承更加灵活的扩展方案,但也存在一些缺点:

    • 开发阶段需要编写很多ConcreteDecorator类型。
    • 行态动态组装带来的结果就是排查故障比较困难,从实际角度看,最后 IComponent的类型是最外层ConcreteDecorator的类型,但它的执行过程是一系列ConcreteDecorator处理后的结果,追踪和调试相对困难。

    动态撤销功能

    在实际场景中,除了动态增加功能,往往还需要动态撤销某些功能,假设用装饰模式来实现英雄联盟中英雄购买装备的过程,买一件装备,就相当于动态为英雄增加功能,但如果后期升级装备需要卖掉一件现有的准备时,在实现上就涉及到这件装备功能的卸载。
    在比如前面代码中的文字处理功能,字体加粗后可以撤销,字体的颜色也支持更换,也需要功能的动态撤销,接上面的例子,实现撤销的功能需要结合后面会学到的状态模式,新增IState接口,引入了状态的概念
    支持撤销功能的代码如下:

    //引入了状态的概念
    public interface IState
    {
        bool Equals(IState newState);
    }
    
    //字体是否加粗可以用bool来表示
    public class BoldState : IState
    {
        public bool IsBold;
        public bool Equals(IState newState)
        {
            if (newState == null)
            {
                return false;
            }
            return ((BoldState)newState).IsBold == IsBold;
        }
    }
    
    //字体颜色的状态比较多
    public class ColorState : IState
    {
        public Color Color = Color.Black;
        public bool Equals(IState newState)
        {
            if (newState == null)
            {
                return false;
            }
            return ((ColorState)newState).Color == Color;
        }
    }
    
    //基本功能
    public interface IText
    {
        string Content { get; }
    }
    
    public class TextObject : IText
    {
        public string Content { get { return "hello"; } }
    
    }
    
    //装饰接口,增加了状态属性和刷新状态的动作
    public interface IDecorator : IText
    {
        IState State { get; set; }
        void Refresh<T>(IState newState) where T : IDecorator;
    }
    
    public abstract class DecoratorBase : IDecorator
    {
        protected IText target;
        public DecoratorBase(IText target)
        {
            this.target = target;
        }
        public abstract string Content { get; }
        public IState State { get; set; }
    
        //更新状态
        public virtual void Refresh<T>(IState newState) where T : IDecorator
        {
            if (this.GetType() == typeof(T))
            {
                if (newState == null)
                {
                    State = null;
                }
                if (State != null && !State.Equals(newState))
                {
                    State = newState;
                }
            }
            if (target != null && typeof(IDecorator).IsAssignableFrom(target.GetType()))
            {
                ((IDecorator)target).Refresh<T>(newState);
            }
        }
    
    }    
    
    public class BoldDecorator : DecoratorBase
    {
        public BoldDecorator(IText target) : base(target)
        {
            base.State = new BoldState();
        }
    
        public override string Content
        {
            get
            {
                if (((BoldState)State).IsBold)
                {
                    return $"<b>{base.target.Content}</b>";
                }
                else
                {
                    return base.target.Content;
                }
            }
        }
    }
    
    public class ColorDecorator : DecoratorBase
    {
        public ColorDecorator(IText target) : base(target)
        {
            base.State = new ColorState();
        }
    
        public override string Content
        {
            get
            {
                if (State != null)
                {
                    string colorName = (((ColorState)State).Color).Name;
                    return $"<{colorName}>{base.target.Content}</{colorName}>";
                }
                else
                {
                    return base.target.Content;
                }      
            }
        }
    }
    

    测试代码

    static void Main(string[] args)
    {
        IText text = new TextObject();
        //默认不加粗、黑色字体
        text = new BoldDecorator(text);
        text = new ColorDecorator(text);
        Console.WriteLine(text.Content);  //< Black > hello </ Black >
    
        //修改为加粗、红色字体
        ColorState colorState = new ColorState();
        colorState.Color = Color.Red;
        BoldState boldState = new BoldState();
        boldState.IsBold = true;
        IDecorator root = (IDecorator)text;
        root.Refresh<ColorDecorator>(colorState);
        root.Refresh<BoldDecorator>(boldState);
        Console.WriteLine(text.Content); //< Red >< b > hello </ b ></ Red >
    
        //取消颜色设置
        colorState = null;
        root.Refresh<ColorDecorator>(colorState);
        Console.WriteLine(text.Content); //< b > hello </ b >
    }
    

    参考书籍:
    王翔著 《设计模式——基于C#的工程化实现及扩展》

  • 相关阅读:
    npm 默认创建项目如何自动配置
    VueJS + TypeScript 入门第一课
    实现类数组转化成数组(DOM 操作获得的返回元素值是一个类数组)
    webpack4(4.41.2) 打包出现 TypeError this.getResolve is not a function
    vue-cli 4.0.5 配置环境变量样例
    关于H5页面在微信浏览器中音视频播放的问题
    ant-design-vue 快速避坑指南
    记elementUI一个大坑
    VUE自定义(有限)库存日历插件
    node转发请求 .csv格式文件下载 中文乱码问题 + 文件上传笔记
  • 原文地址:https://www.cnblogs.com/zhixin9001/p/13347611.html
Copyright © 2020-2023  润新知