温故而知新:设计模式之装饰模式(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)有些相似,只不过区别在于桥模式用于应对多个维度的变化,而装饰模式只用来处理一个维度的变化。

时间: 2024-08-03 02:16:11

温故而知新:设计模式之装饰模式(Decorator)的相关文章

乐在其中设计模式(C#) - 装饰模式(Decorator Pattern)

原文:乐在其中设计模式(C#) - 装饰模式(Decorator Pattern)[索引页][源码下载] 乐在其中设计模式(C#) - 装饰模式(Decorator Pattern) 作者:webabcd 介绍 动态地给一个对象添加一些额外的职责.就扩展功能而言,它比生成子类方式更为灵活. 示例 有一个Message实体类,某个对象对它的操作有Insert()和Get()方法,现在扩展这个对象的功能. MessageModel using System;using System.Collecti

【设计模式】—— 装饰模式Decorator

模式意图 在不改变原来类的情况下,进行扩展. 动态的给对象增加一个业务功能,就功能来说,比生成子类更方便. 应用场景 1 在不生成子类的情况下,为对象动态的添加某些操作. 2 处理一些可以撤销的职责. 3 当不能使用生成子类来扩充时. 模式结构 Component 外部接口,用于定义外部调用的形式.提供默认的处理方法. interface Component{ public void operation(); }   ConcreteComponent  具体的处理类,用于实现operation

设计模式之六(装饰模式)

原文:设计模式之六(装饰模式) 前言 装饰模式:动态の给一个对象添加有些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活. 装饰模式结构图   Component是定义一个对象接口,可以给这些对象动态添加职责 ConcreteComponent是定义了一个具体的对象,也可以给这个对象添加一些职责 Decorator装饰抽象类,继承了Component,从外类来扩展Componnt类的功能,但对于Component来说,是无需知道Decorator的存在的 代码实现 Component类 p

java设计模式之装饰模式详细介绍_java

1.    装饰模式(Decorator)的定义:又名包装(Wrapper)模式,装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案. 2.    装饰模式以对客户端透明的方式动态的给一个对象附加上更多的责任.换言之客户端并不会觉的对象在装饰前和装饰后有什么区别. 3.    装饰模式可以在不创造更多的子类的模式下,将对象的功能加以扩展. 4.    装饰模式与类继承的区别: 1)    装饰模式是一种动态行为,对已经存在类进行随意组合,而类的继承是一种静态的行为,一个类定义成

解读设计模式----装饰模式(Decorator Pattern)

装饰模式(Decorator)也叫包装器模式(Wrapper).以"装饰"的含义生动形象地描绘了"动态地给一个对象添加一些额外的职责"的意图.GOF在<设计模式>一书中给出的定义为:动态地给一个对象添加一些额外的职责.装饰模式充分利用了继承和聚合的优势,创造出无与论比的设计美学.就增加功能来说,Decorator模式相比生成子类更为灵活. UML图: 一.使用Decorator模式的动机 现在有这样一个场景,要求我们为一个对象动态添加新的职责,这个职责并

设计模式:装饰模式(Decorator)

 动态地给一个对象添加一些额外的职责.就增加功能来说,装饰模式想必生成子类更为灵活.  装饰模式又名包装(Wrapper)模式.装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案. 装饰模式中的角色有: 1. 抽象构件角色(Component):给出一个抽象接口,以规范准备接受附加责任的对象. 2. 具体构件角色(ConcreteComponent):定义一个将要接收附加责任的类. 3. 装饰角色(Decorator):持有一个构件(Component)对象的实例,并定义一个与

C++ 设计模式 装饰模式(Decorator Pattern)

C++ 设计模式 装饰模式 在结构型模式中装饰模式给我留下了深刻的印象,其中也感觉到在设计模式中基本都是 依赖C++的多态来实现,装饰模式也不例外,他允许在不改变原有类的代码的基础上, 不通过直接继承原有类的代码通过一个抽象接口层进行实现,甚至可以随意的组合, 所以这里记录之以备使用 下面是模型图: 下面是一个简单的模拟代码,模拟本来一个工具只有写功能,但是我们要不断的扩充其 功能让它有听有读的功能: 这是跑出来的结果 ----source tool---- i can write!! ----

Java设计模式之装饰模式(Decorator模式)介绍_java

Decorator常被翻译成"装饰",我觉得翻译成"油漆工"更形象点,油漆工(decorator)是用来刷油漆的,那么被刷油漆的对象我们称decoratee.这两种实体在Decorator模式中是必须的. Decorator定义:动态给一个对象添加一些额外的职责,就象在墙上刷油漆.使用Decorator模式相比用生成子类方式达到功能的扩充显得更为灵活. 为什么使用Decorator 我们通常可以使用继承来实现功能的拓展,如果这些需要拓展的功能的种类很繁多,那么势必生

设计模式——7装饰模式(Decorator)

7.装饰模式(Decorator)顾名思义,装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例. Source类是被装饰类,Decorator类是一个装饰类,可以为Source类动态的添加一些功能,代码如下:[java] view plaincopy public interface Sourceable { public void method(); } [java] view plaincopy public class