zoukankan      html  css  js  c++  java
  • 装饰者模式

    这篇博客记录一下装饰者模式。


    我们首先借用一下Head First中的样例。来看看装饰者模式涉及的应用场景。

    假设我们须要开发一个饮料计费系统,例如以下图所看到的。


    Beverage作为全部饮料的父类(抽象类或接口均可),
    定义了一个cost方法,用于计算饮料的价格。

    起初定义了四种主要的饮料,HouseBlend、DarkRoast、Decaf和Espresso。
    这些饮料均继承Beverage。并实现各自的cost方法。


    在上文的场景下,假设客户在购买饮料时,
    能够选择性地向基本饮料中加入不同的调料。
    基本饮料加入调料后。就变成了一种“新”的饮料,
    须要又一次实现cost方法。

    假设我们利用继承的方式。实现这些“新”的饮料,
    那么整个计费系统的类图将变成例如以下的结构:

    如上图所看到的,我们仅加入了两种调料milk和tea。
    但整个设计体系中立刻新增了很多子类。
    easy预见。随着基本饮料和调料种类的添加。
    这些子类的数量会进一步增多。达到一个无法维护的数量。

    试想一下,假设某个基本饮料或调料的价格发生改变,
    那么就有很多类涉及的代码须要调整。维护这样一种代码。
    无疑是程序猿的噩梦。


    针对这样的问题。有的朋友可能会这么解决:

    在父类Beverage中添加标志位。来表示是否加入了某种调料。


    同一时候。添加相应的设置和推断接口。
    这么一来,子类在计算价格时。就能够通过父类的接口,
    推断是否加入了某种饮料。然后依据推断结果来计算价格。

    通过这样的方式,就能够大量地降低子类的数量,
    整个设计结构清晰易懂。

    然而。这么设计也有一个致命的缺陷。


    假设新增了调料的种类,那么每一个子类的cost方法还是须要重写。
    同一时候,随着调料种类的添加,子类的cost方法中,
    必定存在大量用于推断是否含有某种饮料的推断语句。

    从总体来看,这样的设计方式还是不够优雅,
    违背了对扩展开发,对改动关闭的设计原则。


    为了解决这类问题,就须要使用本篇博客的主角装饰者模式了。

    整个装饰者的设计思路基本上能够用下图表示:

    如上图所看到的。假设我们须要一个加了Milk和Tea的HouseBlend。
    那么我们能够建立Milk和Tea的类,继承自Beverage。

    在代码执行时。我们能够动态地用Tea来包装HouseBlend,得到的一个Beverage对象;
    然后。继续用Milk来包装这个新的Beverage对象,得到终于的Beverage。
    此时。Milk和Tea类就能够看作HouseBlend的装饰者对象。

    在计算总体的价格时,我们能够直接调用终于的Beverage的cost接口。
    我们已经知道,最外层的实际上是个装饰者对象。


    于是。装饰者对象会进一步调用其持有的Beverage对象的cost接口。
    假设下一个Beverage对象,仍然是个装饰者,
    那么它会进一步调用其持有的Beverage对象的cost接口。

    通过如图所看到的的递归调用,最后将调用到基本饮料的cost接口,
    得到基本饮料的价格。


    然后,在基本饮料价格的基础上。
    逐步添加调料本身的价格,就能够得到终于的价格。

    通过这样的方式,不论调料怎样改变,我们都easy写出清晰简单的代码。


    如今。是时候来看看装饰者模式的定义和结构图了。

    装饰者模式动态地将责任附加到对象上。
    若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

    上图的Component是被装饰对象的父类。
    ConcreteComponent是实际的被装饰对象。

    Decorator是装饰对象的父类或共同接口;
    ConcreteDecoratorA和ConcreteDecoratorB是实际的装饰者对象,
    这些对象将持有Component对象的引用,同一时候作为Component的子类。

    相应装饰者模式的结构图。我们看看上述场景改良后的设计结构:

    对照上文的装饰者模式结构图,新的设计应该是比較easy理解的。


    在本文的最后,我们看看装饰者模式的一个实际应用场景,
    Java IO中输入流设计结构:

    在了解装饰者模式后,再看以下的代码,是不是easy理解的多:

    .............
    InputStream in = new BufferedInputStream (
            new FileInputStream("test.txt"));
    .............
  • 相关阅读:
    目前来看较完美的通用二分法分页存储过程,not in模式,适用于非数值ID,可多字段排序,可以distinct
    SQL分页多主键
    word排版教程技巧
    c# 中的 格式说明符
    关于office第一次打开跳出安装窗口的问题我找到、解决方法了
    SQL 语句时,查询的内联接,外联接,空值和联接
    发布网站类的问题
    ERP失败案例:业务流程再造失误
    开发GUI程序时候调出一个CUI窗口用于调试
    GPL, LGPL...
  • 原文地址:https://www.cnblogs.com/llguanli/p/8617112.html
Copyright © 2011-2022 走看看