• 温故而知新:设计模式之装饰模式(Decorator)


    小时候对日本的动画片十分着迷,“圣斗士”是我的最爱;长大后也曾经一度对“海贼王”十分痴迷;大学看武侠小说时,也特别喜欢那种主人公有奇遇的情况:吃到一颗千年异果,然后功夫大增60年...


    这些个场景都有一个共同点:对象(或系统)会因为一些需求(通常这些需求之间没有任何关联),而扩展自己的功能。具体来说:青铜战士如果有幸能穿上黄金圣衣,不管你是不是黄金圣斗士,在穿上黄金圣衣的那一刻,你就具有黄金圣斗士的能力;海赋王中的人物,如果能吃到一颗奇异果,就能获得特别的能力(比如路飞就是吃了橡胶奇异果);武侠小说中,主角如果不经意间吃下了千年人参,从此功力大增,打遍天下无敌手...

    ok,下面谈谈如何设计,就拿海贼王为例吧,假定系统中有三种奇异果:橡皮奇异果[RubberFruit]--吃了以后身体象橡皮一样有韧性,飞行奇异果[FlyFruit]--吃了以后可以飞起来,金属奇异果[MetalFruit]--吃了以后身体可能变得象金属一样坚硬.


    按常规思路来设计:基本人物可能一种都没吃,也可以吃了几种,我们把各种功能扩展抽象成接口,再分别用基本人物的子类中实现这些接口

    /// <summary>
        /// 橡皮功能接口
        /// </summary>
        public interface IRubberFruit
        {
            void GetAbility();
        }
    
        /// <summary>
        /// 飞行功能接口
        /// </summary>
        public interface IFlyFruit
        {
            void GetAbility();
        }
    
        /// <summary>
        /// 金属功能接口
        /// </summary>
        public interface IMetalFruit
        {
            void GetAbility();
        }
    
        /// <summary>
        /// 人物抽象类
        /// </summary>
        public abstract class Character
        {
    
        }
    
        /// <summary>
        /// 基本人物(无任何特殊能力)
        /// </summary>
        public class NormalCharacter : Character
        {
            public void GetAlility()
            {
                Console.WriteLine("默认基本人物,啥特殊能力都没有!");
            }
        }
    
        /// <summary>
        /// 吃了橡皮果后的人物
        /// </summary>
        public class RubberCharacter : Character, IRubberFruit
        {
            public void GetAbility()
            {
                Console.WriteLine("我的身体可以变得跟橡皮一样有韧性!");
            }
        }
    
    
        /// <summary>
        /// 吃了金属果后的人物
        /// </summary>
        public class MetalCharacter : Character, IMetalFruit
        {
            public void GetAbility()
            {
                Console.WriteLine("如果我愿意,我的身体也可以变得象金属一样硬!");
            }
        }
    
    
        /// <summary>
        /// 吃了飞行果后的人物
        /// </summary>
        public class FlyCharacter : Character, IFlyFruit
        {
            public void GetAbility()
            {
                Console.WriteLine("我能飞!");
            }
        }
    
        /// <summary>
        /// 吃了橡皮果+飞行果后的人物
        /// </summary>
        public class RubberAndFlyCharacter : Character, IRubberFruit, IFlyFruit
        {
            public void GetAbility()
            {
                Console.WriteLine("我的身体可以变得跟橡皮一样有韧性,而且我也能飞!");
            }
        }
    
    
        /// <summary>
        /// 吃了橡皮果+金属果后的人物
        /// </summary>
        public class RubberAndMetalCharacter : Character, IRubberFruit, IMetalFruit
        {
            public void GetAbility()
            {
                Console.WriteLine("我的身体可以变得跟橡皮一样有韧性,如果我愿意,我的身体也可以变得象金属一样硬!");
            }
        }
    
    
        /// <summary>
        /// 吃了金属果+飞行果后的人物
        /// </summary>
        public class MetalAndFlyCharacter : Character, IMetalFruit, IFlyFruit
        {
            public void GetAbility()
            {
                Console.WriteLine("我能飞,如果我愿意,我的身体也可以变得象金属一样硬!");
            }
        }
    
        /// <summary>
        /// 吃了橡皮果+飞行果+金属果后的人物
        /// </summary>
        public class RubberAndFlyAndMetalCharacter : Character, IRubberFruit, IFlyFruit
        {
            public void GetAbility()
            {
                Console.WriteLine("我的身体可以变得跟橡皮一样有韧性,而且我也能飞,如果我愿意,我的身体也可以变得象金属一样硬!");
            }
        }
    

    客户调用示例程序:

    class Program
        {
            static void Main(string[] args)
            {
                RubberCharacter rc = new RubberCharacter();
                rc.GetAbility();
    
                RubberAndFlyAndMetalCharacter rfmc = new RubberAndFlyAndMetalCharacter();
                rfmc.GetAbility();
    
    	    //...
    
                Console.ReadLine();
            }
        }
    

    如果奇异果的种类不增加,这样也没啥问题,但是问题就在于,海贼王中的奇异果肯定不止这几种,随着这种超能力奇异果的种类增加,整个系统很快就会陷入一种类爆炸的局面。
    看看装饰者模式的做法:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace Decorator
    {
        class Program
        {
            static void Main(string[] args)
            {
    
                RubberCharacter rc = new RubberCharacter(new NormalCharacter());
                rc.GetAbility();
               
    
                MetalCharacter rfmc = new MetalCharacter(new FlyCharacter(rc));
                rfmc.GetAbility();
    
                Console.ReadLine();
            }
        }
    
        /// <summary>
        /// 橡皮功能接口
        /// </summary>
        public interface IRubberFruit
        {
            void GetAbility();
        }
    
        /// <summary>
        /// 飞行功能接口
        /// </summary>
        public interface IFlyFruit
        {
            void GetAbility();
        }
    
        /// <summary>
        /// 金属功能接口
        /// </summary>
        public interface IMetalFruit
        {
            void GetAbility();
        }
    
        /// <summary>
        /// 人物抽象类
        /// </summary>
        public abstract class Character
        {
            protected Character _character;
    
            public string Ability { set; get; }
    
    
        }
    
        /// <summary>
        /// 基本人物(无任何特殊能力)
        /// </summary>
        public class NormalCharacter : Character
        {
            public NormalCharacter()
            {
                Ability = "";
            }
            public void GetAlility()
            {
                Console.WriteLine("默认基本人物,啥特殊能力都没有!");
            }
        }
    
        /// <summary>
        /// 吃了橡皮果后的人物
        /// </summary>
        public class RubberCharacter : Character, IRubberFruit
        {
            public RubberCharacter(Character c)
            {
                _character = c;
                Ability += _character.Ability + "我的身体可以变得跟橡皮一样有韧性!";
            }
    
            public void GetAbility()
            {
                
                Console.WriteLine(Ability);
            }
        }
    
    
        /// <summary>
        /// 吃了金属果后的人物
        /// </summary>
        public class MetalCharacter : Character, IMetalFruit
        {
            public MetalCharacter(Character c)
            {
                _character = c;
                Ability += _character.Ability + "如果我愿意,我的身体也可以变得象金属一样硬!";
            }
            public void GetAbility()
            {
                
                Console.WriteLine(Ability);
            }
        }
    
    
        /// <summary>
        /// 吃了飞行果后的人物
        /// </summary>
        public class FlyCharacter : Character, IFlyFruit
        {
            public FlyCharacter(Character c)
            {
                _character = c;
                Ability += _character.Ability + "我能飞!";
            }
            public void GetAbility()
            {
                
                Console.WriteLine(Ability);
            }
        }
    
    
    }
    
    

    接口部分并没有变,关键就在于实现接口的各个类,巧妙的用对象组合方式解决了类爆炸的问题(有心的读者可以比较一下前后类的数量变化),这样在客户程序调用时,可以把原来的对象层层包裹,附加上一层又一层的功能,最后给出该模式的意图:

    意图

    动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。[GOF 《设计模式》]

    类图如下:

    后记:该模式其实与桥接模式(Bridge)有些相似,只不过区别在于桥模式用于应对多个维度的变化,而装饰模式只用来处理一个维度的变化。

    作者:菩提树下的杨过
    出处:http://yjmyzz.cnblogs.com
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    [linux] unzip把压缩文件解压到指定目录下 d
    在线客服系统全渠道接入不限制坐席、域名、服务器GOFLY在线客服系统
    [html] link 标签 rel=canonical 属性的用法
    [javascript] 获取当前时间日期和时间戳
    [css] 加载本地文件出现跨域Cross origin requests are only supported for protocol schemes: http, data, chrome, chromeextension, chromeuntrusted, https.
    [git] git stash 暂存未提交的修改文件
    [MySQL系列] mysql find_in_set搜索以逗号分隔的字符串
    [Linux系列] Vim命令清空文件删除所有内容
    [Golang系列] gorm执行like模糊查询
    [Linux系列] 在线客服系统代码脚本getconf LONG_BIT获取当前系统的位数
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/1668963.html
Copyright © 2020-2023  润新知