2021-08-03 22:18 浏览: 9 次 来源: IT服务圈儿
来源地址: https://mp.weixin.qq.com/s/K5YVuzDugmpUiPSqzui4CQ
免责声明: 本栏目资源为转载资源如有侵权请联系 企鹅:826319429 告知本站将及时删除谢谢!
作者:宋学方
来源:IT服务圈儿原创作品,转载请联系微信jb_quaner。
装饰器模式的说明:动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。原文是:
Attach additional responsibilities to an object dynamically keeping the same interface.Decorators provide a flexible alternative to subclassing for extending functionality.
从这段话可以看到装饰器的特点:动态地为对象增加新的功能,即扩展原有对象的功能。
装饰器的优点,至少是相比于继承来说,“更有弹性”,怎么理解呢,装饰器模式相比生成子类更为灵活,简单理解就是继承扩展是静态的,是在编译期间确定的,而装饰器是“动态”的,是在运行时灵活装配出来的。
就以美国电影《钢铁侠》为例,如果托尼·史塔克在没有造出战衣之前,在考虑想拥有一个能够喷火的功能,那么如果采用继承的方式,则需要他生下一个儿子,该儿子通过基因变异出现了喷火功能,如果他又想拥有飞行功能怎么办呢,原来那个儿子的基因已经确定了,不可能拥有飞行的功能了,怎么办呢,只能再生一个儿子,拥有飞行的基因才行,想想都不灵活。这时候装饰模式来解决这个问题,托尼·史塔克不需要“生儿子”动作那么大,只需要打造一个支持扩展功能的钢铁战衣(装饰器)就可以,他自身相当于是具体的待装饰的组件,当需要喷火时,只需要在战衣上增加喷火器就可以,如果又想飞行,只需要在战衣上增加飞行装置就行,当然,如果不想喷火了,拆掉就行了,灵活。
如下图所示为继承图:
如下图所示为装饰器模式图:
按照UML图来展示如下图所示:
代码如下所示:
1.人形接口定义了所有实现人形接口的子类均需要实现action方法。
public interface IPeopleLikeInterface {
public void action();
}
2.TonyStark子类实现了IPeopleLikeInterface接口,定义了TonyStark所具有的行为能力:
public class TonyStark implements IPeopleLikeInterface {
@Override
public void action() {
System.out.println("I am TonyStark");
}
}
3.BattleSuit抽象类,同样实现了IPeopleLikeInterface 接口,与TonyStark不同的是,BattleSuit抽象类还包括一个IPeopleLikeInterface 接口的引用,该引用就是需要被装饰(或被增强)的对象的引用。
public class BattleSuit implements IPeopleLikeInterface {
private IPeopleLikeInterface people;
@Override
public void action() {
}
public IPeopleLikeInterface getPeople() {
return people;
}
public void setPeople(IPeopleLikeInterface people) {
this.people = people;
}
}
4.相应的FireSuit子类,继承自BattleSuit抽象类,实现了自己的action方法,该行为就是增强或装饰的功能。
public class FireSuit extends BattleSuit {
@Override
public void action()
{
super.getPeople().action();
System.out.println("Fire on !!");
}
}
5.与FireSuit子类类似的是可飞行功能增强,如下所示:
public class FlySuit extends BattleSuit {
@Override
public void action()
{
super.getPeople().action();
System.out.println("Fly !!");
}
}
至此对象间的结构构建完成,那么在实际使用过程中是如何动态增强TonyStark的功能的呢,如下图所示:
public class DecorateRun {
public void main(String[] args)
{
/**
* 初始化一个TonyStark对象
*/
IPeopleLikeInterface tonyStark = new TonyStark();
/**
* 当需要使TonyStark具有喷火功能时
*/
BattleSuit battleSuit = new FireSuit();
battleSuit.setPeople(tonyStark);
/**
* 使用喷火功能
*/
battleSuit.action();
/**
* 当需要使TonyStark具有飞行功能时
*/
BattleSuit battleSuit2 = new FlySuit();
battleSuit.setPeople(tonyStark);
/**
* 使用飞行功能
*/
battleSuit2.action();
}
}
好了,接下来我们看一下成熟的java 源码中流机制是如何使用装饰模式的。
java语言采用流的机制来实现输入/输出。所谓流,就是数据的有序流动,流可以是从某个源出来,到某个目的地去。根据流的方向可以将流分成输出流和输入流。程序通过输入流读取数据,通过输出流写出数据。例如:一个java程序可以使用FileInputStream类从一个磁盘文件读取数据,如下图:
我们先看一下Java输入流的类层次结构,如下图所示:
我们以红框标识的类为例,说明Java输入流如何使用装饰器设计模式。
首先看一下这几个类的用途。
我们以红框标识的类为例,说明Java输入流如何使用装饰器设计模式。
首先看一下这几个类的用途。
1.抽象类InputStream,是所有输入流的基类。
public abstract class InputStream implements Closeable
2.FileInputStream,从磁盘文件中直接读取文件,每次读取一个字节。
public
class FileInputStream extends InputStream
/**
* Reads a byte of data from this input stream. This method blocks
* if no input is yet available.
*
* @return the next byte of data, or <code>-1</code> if the end of the
* file is reached.
* @exception IOException if an I/O error occurs.
*/
public int read() throws IOException {
return read0();
}
如下图所示为最基础用法:
FileInputStream inputStream = new FileInputStream("d://1.txt");
int len;
/**
* 一次读取字节,每读取一个字节都要实现一次与硬盘的交互操作
*
*/
while ((data= inputStream.read()) != -1) {
}
当然也可以每次读取更多字节,以减少与硬盘的IO交互次数,如下图所示:
FileInputStream inputStream = new FileInputStream("d://1.txt");
int len;
byte[] bs = new byte[1024];
//这里添加了一个缓存数组,每次从硬盘读取1024个字节,也就是说,每读取1024个字节才与硬盘实现一次交互
while ((len = inputStream.read(bs)) != -1) {
}
实际上java提供了一种更为快速的实现方案,即引入BufferInputStream,此时调用read方法仍然是获取1字节,但是有内存缓存中读取,超过8k后,再与硬盘做次交互,一次性读取缓存中8k如下图所示:
FileInputStream inputStream = new FileInputStream("d://1.txt");
BufferedInputStream bis = new BufferedInputStream(inputStream); //默认有8k的缓存
int len;
byte[] bs = new byte[1024];
while ((len = bis.read()) != -1) {
}
很显然,BufferInputStream就是一个装饰器,FileInputStream一是原始对象,类似于TonyStark的角色,通过BufferInputStream来对FileInputStream的功能进行增强。
我们看一下InputStream,FileInputStram、BufferInputStream之间的依赖关系图,看是否是装饰器模式。
可以看到FilterInputStream持有一个InputStream的引用,具备了增强的类结构,
BufferedInputStream、DataInputStream、LineNumberInputStream、PushbackInputStream 均是实现FilterInputStream的具体装饰类,用于装饰或增强原始对象的功能。
至此,我们再搬出装饰器模式的各角色定义,如上图所标示:
(1)抽象构件,如InputStream类,抽象类,定义统一的接口。
(2) 具体构建(Concrete Component)角色:如FileInputStream原始流处理器扮演。它们实现了抽象构建角色所规定的接口,可以被装饰流处理器所装饰。
(3) 抽象装饰(Decorator)角色:如FilterInputStream,它实现了InputStream所规定的接口。(4)具体装饰(Concrete Decorator)角色,如DateInputStream、BufferedInputStream 等,实现原始对象功能增强。
宋学方 一个略懂一二但饱含热情的编码人
作者:宋学方
原文链接:https://blog.csdn.net/songxuefang/article/details/105916838
声明:本文为 程序员架构 原创投稿,未经允许请勿转载。
点分享
点点赞
点在看