装饰者模式

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


我们首先借用一下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"));
.............
【推广】 免费学中医,健康全家人
原文地址:https://www.cnblogs.com/llguanli/p/8617112.html