博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[原译]理解并实现装饰器模式
阅读量:6828 次
发布时间:2019-06-26

本文共 4799 字,大约阅读时间需要 15 分钟。

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

背景

本文讨论装饰器模式,这个模式是因为很多情况下需要动态的给对象添加功能.比如我们创建了一个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下载

原文地址:

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

转载于:https://www.cnblogs.com/lazycoding/archive/2012/10/22/2733639.html

你可能感兴趣的文章
正则表达式
查看>>
在 JS 中使用 fetch 更加高效地进行网络请求
查看>>
javascript 分页算法
查看>>
android手机root后的安全问题
查看>>
bat改ip
查看>>
SpringBoot之在Servlet2.5容器中部署war应用
查看>>
项目申请文档提纲
查看>>
加密解密第二章:ollydbg用法
查看>>
百万PV网站架构
查看>>
N26-第四周作业
查看>>
在vmware安装Ubuntu桌面软件
查看>>
MySQL之用户和权限管理
查看>>
常用的命令的使用方法
查看>>
使用HeartBeat实现高可用HA的配置过程详解
查看>>
最常用的四种大数据分析方法
查看>>
ajax https请求返回json数据
查看>>
convenience - 便利构造函数
查看>>
golang 碎片整理之 结构体
查看>>
查看oracle查看当前连接以及修改最大连接数
查看>>
docker安装mysql镜像
查看>>