zoukankan      html  css  js  c++  java
  • [原译]理解并实现装饰器模式

    著作权声明:本文由http://leaver.me 翻译,欢迎转载分享。请尊重作者劳动,转载时保留该声明和作者博客链接,谢谢!

    背景
    本文讨论装饰器模式,这个模式是因为很多情况下需要动态的给对象添加功能.比如我们创建了一个Stream类.后来需要对这个数据流类动态的添加一个加密功能.有人可能说把加密方法写到流类里面啊.然后使用一个bool变量来控制开关就行了.但是这样.这个加密方法只能写一种..如果用派生类来实现.那么..对于不同的加密方法.,都要创建一个子类,举个例子.比如有时候是一些函数的组合.我们最终的派生类的数目基本上就和排列组合的数目一样了.

    我们使用装饰器模式来解决这个问题.GoF描述为
    "Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality."

    首先看一下图.理解一下这个模式中每一个类的作用


    • Component:定义了可以动态添加功能的具体类ConcreteComponents的接口.
    • ConcreteComponent: 可以动态添加功能的具体类
    • Decorator: 定义了动态添加到ConcreteComponent类中的功能的接口
    • ConcreteDecorator: 可以添加到 ConcreteComponent.中的具体功能类.

    使用代码

    我们开一个面包店的例子.面包店卖蛋糕和甜点.客户可以买蛋糕和甜点,同时添加一些额外的东西.额外的东西包括奶油(Cream),樱桃(Cherry),香料(Scent)和会员(Name Card)

    如果我们用派生类来实现..那么我们会有如下的类
    • CakeOnly
    • CakeWithCreamAndCherry
    • CakeWithCreamAndCherryAndScent
    • CakeWithCreamAndCherryAndScentAndNameCard
    • CakeWithCherryOnly
    • PastryOnly
    • PastryWithCreamAndCherry
    • PastryWithCreamAndCherryAndScent
    • PastryWithCreamAndCherryAndScentAndNameCard
    • PastryWithCherryOnly
    • 等等等等

    这简直就是噩梦..我们用装饰器模式来实现把.
    首先定义Component 接口

    public abstract class BakeryComponent
    {
        public abstract string GetName();
        public abstract double GetPrice();
    }

    前面说过了.这个类定义了能够动态添加功能的具体类(ConcreteComponents)的接口,好吧.然后来创建具体类ConcreteComponents

    class CakeBase : BakeryComponent
    {
        // 真实世界里,这个值应该来自数据库等
        private string m_Name = "Cake Base";
        private double m_Price = 200.0;
    
        public override string GetName()
        {
            return m_Name;
        }
    
        public override double GetPrice()
        {
            return m_Price;
        }
    }
    
    class PastryBase : BakeryComponent
    {
        //真实世界里,这个值应该来自数据库等
        private string m_Name = "Pastry Base";
        private double m_Price = 20.0;
    
        public override string GetName()
        {
            return m_Name;
        }
    
        public override double GetPrice()
        {
            return m_Price;
        }
    }

    现在基对象准备好了.看看那些可以被动态添加的功能.我们看看Decorator 类

    public abstract class Decorator : BakeryComponent
    {
        BakeryComponent m_BaseComponent = null;
        
        protected string m_Name = "Undefined Decorator";
        protected double m_Price = 0.0;
    
        protected Decorator(BakeryComponent baseComponent)
        {
            m_BaseComponent = baseComponent;
        }
    
        #region BakeryComponent Members
    
        string BakeryComponent.GetName()
        {
            return string.Format("{0}, {1}", m_BaseComponent.GetName(), m_Name);
        }
    
        double BakeryComponent.GetPrice()
        {
            return m_Price + m_BaseComponent.GetPrice();
        }
        #endregion
    }

    注意两个地方.第一个就是类实现BakeryComponent 接口,原因是装饰后的蛋糕还是蛋糕,另一个是该类也持有一个BakeryComponent 对象,原因是,我们需要Cake和装饰的项目是is-a关系,但是事实上不是.通过加一个对象就可以模拟is-a关系.

    一句话.我们使用继承实现了静态的is-a关系,而是用构成则是一个动态的is-a关系.

    然后看看ConcreteDecorators 如何实现

    class ArtificialScentDecorator : Decorator
    {
        public ArtificialScentDecorator(BakeryComponent baseComponent)
            : base(baseComponent)
        {
            this.m_Name = "Artificial Scent";
            this.m_Price = 3.0;
        }
    }
    
    class CherryDecorator : Decorator
    {
        public CherryDecorator(BakeryComponent baseComponent)
            : base(baseComponent)
        {
            this.m_Name = "Cherry";
            this.m_Price = 2.0;
        }
    }
    
    class CreamDecorator : Decorator
    {
        public CreamDecorator(BakeryComponent baseComponent)
            : base(baseComponent)
        {
            this.m_Name = "Cream";
            this.m_Price = 1.0;
        }
    }

    然后看一下如何给一个会员卡添加一个打折的信息.

    class NameCardDecorator : Decorator
    {
        private int m_DiscountRate = 5;
    
        public NameCardDecorator(BakeryComponent baseComponent)
            : base(baseComponent)
        {
            this.m_Name = "Name Card";
            this.m_Price = 4.0;
        }
    
        public override string GetName()
        {
            return base.GetName() + 
                string.Format("\n(Please Collect your discount card for {0}%)", 
                m_DiscountRate);
        }        
    }

    现在我们的客户端可使用Decorator 来装饰ConcreteComponents 生成不同的组合.看看例子

    static void Main(string[] args)
    {
        // 创建一个基本的蛋糕
        CakeBase cBase = new CakeBase();
        PrintProductDetails(cBase);
    
        // 添加奶油
        CreamDecorator creamCake = new CreamDecorator(cBase);
        PrintProductDetails(creamCake);
        
        // 添加樱桃
        CherryDecorator cherryCake = new CherryDecorator(creamCake);
        PrintProductDetails(cherryCake);
    
        // 添加香料
        ArtificialScentDecorator scentedCake = new ArtificialScentDecorator(cherryCake);
        PrintProductDetails(scentedCake);
    
        // 添加一张会员卡
        NameCardDecorator nameCardOnCake = new NameCardDecorator(scentedCake);
        PrintProductDetails(nameCardOnCake);
        
        // 添加一个甜点
        PastryBase pastry = new PastryBase();
        PrintProductDetails(pastry);
    
        // 添加 奶油和樱桃
        CreamDecorator creamPastry = new CreamDecorator(pastry);
        CherryDecorator cherryPastry = new CherryDecorator(creamPastry);
        PrintProductDetails(cherryPastry);
    }

    运行效果


    看看我们的装饰器模式例子的类图结构

    亮点在那里
    装饰器模式是很典型的开放-封闭原则的例子.我们的类对扩展开放,而对修改封闭.
    Demo下载
    DecoratorSampleApp.zip

    原文地址:UnderstandingplusandplusImplementingplusDecoratorp
    著作权声明:本文由http://leaver.me 翻译,欢迎转载分享。请尊重作者劳动,转载时保留该声明和作者博客链接,谢谢!

  • 相关阅读:
    origin 8.5 曲线拟合,延长曲线范围
    赤手空拳编写C#代码
    Vmware 10安装MAC OS X 10.9备忘
    Contest 1445
    Contest 1428
    Contest 1435
    Contest 991
    CSP-SJX2019 解题报告
    【BZOJ4817】【SDOI2017】树点染色
    codeforce 804B Minimum number of steps
  • 原文地址:https://www.cnblogs.com/lazycoding/p/2733639.html
Copyright © 2011-2022 走看看