装饰模式:装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性。动态给一个对象增加功能,这些功能可以再动态的撤消。增加由一些基本功能的排列组合而产生的非常大量的功能。是在不必改变原类文件和不使用继承的情况下,动态的扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象

趣味解释:DECORATOR—Mary过完轮到Sarly过生日,还是不要叫她自己挑了,不然这个月伙食费肯定玩完,拿出我去年在华山顶上照的照片,在背面写上“最好的的礼物,就是爱你的Fita”,再到街上礼品店买了个像框(卖礼品的MM也很漂亮哦),再找隔壁搞美术设计的Mike设计了一个漂亮的盒子装起来……,我们都是Decorator,最终都在修饰我这个人呀,怎么样,看懂了吗?趣味解释参见:http://blog.jobbole.com/20496/

Java代码中实现:以Java中的输入输出流为例,Java中的处理流即是对节点流的包装和装饰

package decorator;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.PushbackInputStream;
import java.io.PushbackReader; public class IOTest {
/* test.txt内容:
* hello world!
*/
public static void main(String[] args) throws IOException, ClassNotFoundException {
//文件路径可自行更换
final String filePath = "F:/eclipse/workspace/decorator/Test.txt"; //InputStream相当于被装饰的接口或者抽象类,FileInputStream相当于原始的待装饰的对象,FileInputStream无法装饰InputStream
//另外FileInputStream是以只读方式打开了一个文件,并打开了一个文件的句柄存放在FileDescriptor对象的handle属性
//所以下面有关回退和重新标记等操作,都是在堆中建立缓冲区所造成的假象,并不是真正的文件流在回退或者重新标记
InputStream inputStream = new FileInputStream(filePath);
final int len = inputStream.available();//记录一下流的长度
System.out.println("FileInputStream不支持mark和reset:" + inputStream.markSupported()); System.out.println("---------------------------------------------------------------------------------"); /* 下面分别展示三种装饰器的作用BufferedInputStream,DataInputStream,PushbackInputStream,LZ下面做了三个装饰器的功能演示 */ //首先装饰成BufferedInputStream,它提供我们mark,reset的功能
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);//装饰成 BufferedInputStream
System.out.println("BufferedInputStream支持mark和reset:" + bufferedInputStream.markSupported());
bufferedInputStream.mark(0);//标记一下
char c = (char) bufferedInputStream.read();
System.out.println("Test.txt文件的第一个字符:" + c);
bufferedInputStream.reset();//重置
c = (char) bufferedInputStream.read();//再读
System.out.println("重置以后再读一个字符,依然会是第一个字符:" + c);
bufferedInputStream.reset(); System.out.println("---------------------------------------------------------------------------------"); //装饰成 DataInputStream,我们为了又使用DataInputStream,又使用BufferedInputStream的mark reset功能,所以我们再进行一层包装
//注意,这里如果不使用BufferedInputStream,而使用原始的InputStream,read方法返回的结果会是-1,即已经读取结束
//因为BufferedInputStream已经将文本的内容读取完毕,并缓冲到堆上,默认的初始缓冲区大小是8192B
DataInputStream dataInputStream = new DataInputStream(bufferedInputStream);
dataInputStream.reset();//这是BufferedInputStream提供的功能,如果不在这个基础上包装会出错
System.out.println("DataInputStream现在具有readInt,readChar,readUTF等功能");
int value = dataInputStream.readInt();//读出来一个int,包含四个字节
//我们转换成字符依次显示出来,可以看到LZ文件的前四个字符
String binary = Integer.toBinaryString(value);
int first = binary.length() % 8;
System.out.print("使用readInt读取的前四个字符:");
for (int i = 0; i < 4; i++) {
if (i == 0) {
System.out.print(((char)Integer.valueOf(binary.substring(0, first), 2).intValue()));
}else {
System.out.print(((char)Integer.valueOf(binary.substring(( i - 1 ) * 8 + first, i * 8 + first), 2).intValue()));
}
}
System.out.println(); System.out.println("---------------------------------------------------------------------------------"); //PushbackInputStream无法包装BufferedInputStream支持mark reset,因为它覆盖了reset和mark方法
//因为流已经被读取到末尾,所以我们必须重新打开一个文件的句柄,即FileInputStream
inputStream = new FileInputStream(filePath);
PushbackInputStream pushbackInputStream = new PushbackInputStream(inputStream,len);//装饰成 PushbackInputStream
System.out.println("PushbackInputStream装饰以后支持退回操作unread");
byte[] bytes = new byte[len];
pushbackInputStream.read(bytes);//读完了整个流
System.out.println("unread回退前的内容:" + new String(bytes));
pushbackInputStream.unread(bytes);//再退回去
bytes = new byte[len];//清空byte数组
pushbackInputStream.read(bytes);//再读
System.out.println("unread回退后的内容:" + new String(bytes)); System.out.println("---------------------------------------------------------------------------------"); /* 以上有两个一层装饰和一个两层装饰,下面我们先装饰成Reader,再进行其它装饰 */ //由于之前被PushbackInputStream将流读取到末尾,我们需要再次重新打开文件句柄
inputStream = new FileInputStream(filePath);
InputStreamReader inputStreamReader = new InputStreamReader(inputStream,"utf-8");//先装饰成InputStreamReader
System.out.println("InputStreamReader有reader的功能,比如转码:" + inputStreamReader.getEncoding()); System.out.println("---------------------------------------------------------------------------------"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader);//我们进一步在reader的基础上装饰成BufferedReader
System.out.println("BufferedReader有readLine等功能:" + bufferedReader.readLine()); System.out.println("---------------------------------------------------------------------------------"); LineNumberReader lineNumberReader = new LineNumberReader(inputStreamReader);//我们进一步在reader的基础上装饰成LineNumberReader
System.out.println("LineNumberReader有设置行号,获取行号等功能(行号从0开始),当前行号:" + lineNumberReader.getLineNumber()); System.out.println("---------------------------------------------------------------------------------"); //此处由于刚才被readLine方法将流读取到末尾,所以我们再次重新打开文件句柄,并需要将inputstream再次包装成reader
inputStreamReader = new InputStreamReader(new FileInputStream(filePath));
PushbackReader pushbackReader = new PushbackReader(inputStreamReader,len);//我们进一步在reader的基础上装饰成PushbackReader
System.out.println("PushbackReader是拥有退回操作的reader对象");
char[] chars = new char[len];
pushbackReader.read(chars);
System.out.println("unread回退前的内容:" + new String(chars)); pushbackReader.unread(chars);//再退回去
chars = new char[len];//清空char数组
pushbackReader.read(chars);//再读
System.out.println("unread回退后的内容:" + new String(chars)); } }

  运行结果:

FileInputStream不支持mark和reset:false
---------------------------------------------------------------------------------
BufferedInputStream支持mark和reset:true
Test.txt文件的第一个字符:H
重置以后再读一个字符,依然会是第一个字符:H
---------------------------------------------------------------------------------
DataInputStream现在具有readInt,readChar,readUTF等功能
使用readInt读取的前四个字符:Hell
---------------------------------------------------------------------------------
PushbackInputStream装饰以后支持退回操作unread
unread回退前的内容:Hello World!
unread回退后的内容:Hello World!
---------------------------------------------------------------------------------
InputStreamReader有reader的功能,比如转码:UTF8
---------------------------------------------------------------------------------
BufferedReader有readLine等功能:Hello World??
---------------------------------------------------------------------------------
LineNumberReader有设置行号,获取行号等功能(行号从0开始),当前行号:0
---------------------------------------------------------------------------------
PushbackReader是拥有退回操作的reader对象
unread回退前的内容:Hello World!

上图中画出了输入输出流的类图,和装饰器模式对照。其中InputStreamReader转换流比较特殊,即是InputStream输入流装饰产生,同时又是Reader字符流的装饰原始类。

分析此图可以看出:装饰器模式的灵活,我们创建的一个FileInputstream对象,我们可以使用各种装饰器让它具有不同的特别的功能,这正是动态扩展一个类的功能的最佳体现,而装饰器模式的灵活性正是JAVA中IO所需要的,不得不赞一下JAVA类库的建造者实在是强悍。上述的XXXXInputStream的各个类都继承了InputStream,这样做不仅是为了复用InputStream的父类功能(InputStream也是一种模板方法模式,它定义了read(byte[])方法的简单算法,并将read()方法交给具体的InputStream去实现),也是为了可以重叠装饰,即装饰器也可以再次被装饰,而过渡到Reader以后,Reader的装饰器体系则是类似的。

总结:装饰器模式就是一个可以非常灵活的动态扩展类功能的设计模式,它采用组合的方式取代继承,使得各个功能的扩展更加独立和灵活。

参考文章:http://www.cnblogs.com/zuoxiaolong/p/pattern11.html

Java--23种设计模式之decorator模式的更多相关文章

  1. java的23种设计模式之建造者模式

    场景和本质 场景 本质 案例 原理 应用场景 场景和本质 场景 我们要建造一个复杂的产品.比如:神州飞船,Iphone.这个复杂的产品的创建.有这样一个问题需要处理:装配这些子组件是不是有个步骤问题? ...

  2. 实践GoF的23种设计模式:建造者模式

    摘要:针对这种对象成员较多,创建对象逻辑较为繁琐的场景,非常适合使用建造者模式来进行优化. 本文分享自华为云社区<[Go实现]实践GoF的23种设计模式:建造者模式>,作者: 元闰子. 简 ...

  3. JAVA开发的23种设计模式之 --- 桥接模式

    桥接模式 概述:将抽象部分与他的实现部分分离,这样抽象化与实现化解耦,使他们可以独立的变化.如何实现解耦的呢,就是通过提供抽象化和实现化之间的桥接结构.    应用场景        实现系统可能有多 ...

  4. java 23种设计模式及具体例子 收藏有时间慢慢看

    设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代 码可靠性. 毫无疑问,设计模式 ...

  5. java 23 种设计模式

    一.设计模式的分类 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接 ...

  6. (转)java 23种设计模式

    设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了 ...

  7. java 23种设计模式,一般情况下,常用的有哪些? 转载

    原址:http://wangle.iteye.com/blog/196972 工厂模式, 工厂方法模式,单例模式, 外观(Facade)模式, 观察者(Observer)模式,桥接(Bridge)模式 ...

  8. Java 23种设计模式

    转自: http://zz563143188.iteye.com/blog/1847029 ; i<count; i++){ list.add(new MailSender()); } } pu ...

  9. java 23种设计模式 深入浅出

    以下内容只作为对自己对知识进行总结,如有引用他人文章会在文段末尾表明出处: Java的23种设计模式 23种设计模式总共可以分为三大类,进行不定期更新总结,将逐步展开介绍自己对设计模式的理解,多多指教 ...

  10. Java 23 种设计模式的分类和功能

    设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路.它不是语法规定,而是一套用来提高代码可复用性.可维护性.可读性.稳健性以及安全性的解决方案. 设计模式 ...

随机推荐

  1. 【凯子哥带你夯实应用层】使用ActionProvider实现子菜单时遇到的一个坑

    转载请注明出处:http://blog.csdn.net/zhaokaiqiang1992 近期在重温Android基础.在看到ActionProvider的时候遇到一个坑.分享到大家,避免入坑. 首 ...

  2. HDU 3564 Another LIS splay(水

    题意: 给定一个空序列 插入n个数(依次插入 1.2.3.4··n) 以下n个数表示i插在哪个位置. 每插入一个数后输出这个序列的lis 然后... 由于每次插入的数都是当前序列最大的数 所以不会影响 ...

  3. Python实战之自己主动化评论

    Python实战之自己主动化评论 玩csdn博客一个多月了,渐渐发现了一些有意思的事,常常会有人用相同的评论到处刷.不知道是为了加没什么用的积分,还是纯粹为了表达楼主好人.那么问题来了,这种无聊的事情 ...

  4. Kubernetes基本概念之Label

    系列目录 在为对象定义好Label后,其他对象就可以通过Label来对对象进行引用.Label的最常见的用法便是通过spec.selector来引用对象. apiVersion: v1 kind: R ...

  5. Model Vaildation

    https://docs.asp.net/en/latest/mvc/models/validation.html 许多有用的验证属性都必须引用命名空间: System.ComponentModel. ...

  6. asyncio协程与并发

    并发编程 Python的并发实现有三种方法. 多线程 多进程 协程(生成器) 基本概念 串行:同时只能执行单个任务 并行:同时执行多个任务 在Python中,虽然严格说来多线程与协程都是串行的,但其效 ...

  7. Java Web工作原理(转载)

    知识要点: 1.HTTP协议 2.web服务器的缺陷及其解决方案 3.对Servlet的认识 4.Servlet的主要任务 5.web容器对Servlet的支持包括的内容 HTTP协议---(Hype ...

  8. 800元组装一台3D打印机全教程流程-零件清单

    继前面的教程800元组装一台3D打印机全教程流程 k800是一台根据kosselmini改进的低成本3d打印机,通过改变设计,降低了成本,但损失较少性能,取得性价比. 主要改动是:底部支架改为-> ...

  9. AAC包增加ADTS头Without MediaCodec

    AAC原始码流无法直接播放,一般需要封装为ADTS格式才能再次使用,本博主在android中用MediaCodec编码得到的AAC就是raw格式,为了保存为.aac格式,需要增加adts头,这样就可以 ...

  10. 5 Ways to Send Email From Linux Command Line

    https://tecadmin.net/ways-to-send-email-from-linux-command-line/ We all know the importance of email ...