目录与学习目标
1:从IO引入(奇怪的IO类)2:基于继承的设计方案3:基于装饰器模式的设计方案4:实战例子(对IO类的各种增强)5:实战例子(结束订单的积分抵扣) 6:装饰器小结
1:从IO引入(奇怪的IO类)
Java IO 类库非常庞大和复杂,有几十个类,负责 IO 数据的读取和写入。如果对 Java IO 类做一下分类,我们可以从下面两个维度将它划分为四类。具体如下所示:
|
字节流 |
字符流 |
| 输入流 |
InputStream |
Reader |
| 输出流 |
OutputStream |
Writer |
基于这4个父类上 扩展出很多子类

曾经对 Java IO 的一些用法产生过很大疑惑:我们打开文件 test.txt,从中读取数据。其中,InputStream 是一个抽象类,FileInputStream 是专门用来读取文件流的子类。BufferedInputStream 是一个支持带缓存功能的数据读取类,可以提高数据读取的效率。需要先创建一个 FileInputStream 对象,然后再传递给 BufferedInputStream 对象来使用。Java IO 为什么不设计一个继承 FileInputStream 并且支持缓存的 BufferedFileInputStream 类
InputStream inputStream = new FileInputStream("A:\\test.txt"); InputStream bufferedInputStream = new BufferedInputStream(inputStream); byte[] data = new byte[128]; while (bufferedInputStream.read(data) != -1) { //... }
//继承 FileInputStream 并且支持缓存的 BufferedFileInputStream 类 InputStream bufferedFileInputStream = new BufferedFileInputStream("A:\\test.txt"); byte[] data = new byte[128]; while (bufferedFileInputStream.read(data) != -1) { //...}
2:基于继承的设计方案
如果 InputStream 只有一个子类 FileInputStream 的话,那我们在 FileInputStream 基础之上,再设计一个孙子类 BufferedFileInputStream,也算是可以接受的,毕竟继承结构还算简单。但实际上,继承 InputStream 的子类有很多。我们需要给每一个 InputStream 的子类,再继续派生支持缓存读取的子类。除了支持缓存读取之外,如果我们还需要对功能进行其他方面的增强,比如下面的 DataInputStream 类,支持按照基本数据类型(int、boolean、long 等)来读取数据。
InputStream inputStream = new FileInputStream("A:\\test.txt"); DataInputStream dataInputStream = new DataInputStream(inputStream); int data = dataInputStream.readInt();
//继承 FileInputStream 并且支持基本数据类型的 dataFileInputStream 类 InputStream dataFileInputStream = new DataFileInputStream("A:\\test.txt"); int data = dataFileInputStream.readInt();
假如说这时候需要一个支持缓存、又支持按照基本类型读取数据的类,那就要再继续派生出 BufferedDataFileInputStream 那么后面会变得没完没了。
3:基于装饰器模式的设计方案
之前还讲到“组合优于继承”,可以“使用组合来替代继承”。针对刚刚的继承结构过于复杂的问题,我们可以通过将继承关系改为组合关系来解决。下面的代码展示了 Java IO 的这种设计思路。 那装饰器模式就是简单的“用组合替代继承”吗?当然不是。从 Java IO 的设计来看,装饰器模式相对于简单的组合关系,通常两个比较特殊的地方。注意要有共同的接口类 或者 抽象类1:第一个比较特殊的地方是: 装饰器类和原始类继承同样的父类,这样我们可以对原始类“嵌套”多个装饰器类。 我们对 FileInputStream 嵌套了两个装饰器类: BufferedInputStream 和 DataInputStream,让它既支持缓存读取,又支持按照基本数据类型来读取数据。第二个比较特殊的地方是: 装饰器类是对功能的增强,这也是装饰器模式应用场景的一个重要特点。 拿比较相似的代理模式和装饰器模式来对比: 代理模式中,代理类附加的是跟原始类无关的功能, 装饰器模式中,装饰器类附加的是跟原始类相关的增强功能。
4:实战例子(对IO类的各种增强)
public class BufferedInputStream extends InputStream { protected volatile InputStream in; protected BufferedInputStream(InputStream in) { System.out.println("提供支持缓存读取数据 功能"); this.in = in; } //...实现基于缓存的读数据接口... @Override public int read() throws IOException { return 0; }}public class DataInputStream extends InputStream { protected volatile InputStream in; protected DataInputStream(InputStream in) { System.out.println("提供基本类型读取数据 功能"); this.in = in; } //...实现读取基本类型数据的接口 @Override public int read() throws IOException { return 0; }}
public class FileInputStreamDemo { public void FileInputStreamDemo() throws IOException { InputStream inputStream = new FileInputStream("A:\\test.txt"); //BufferedInputStream 继承了 InputStream 提供支持缓存读取数据 功能 InputStream bufferedInputStream = new BufferedInputStream(inputStream); //DataInputStream 继承了 InputStream 提供基本类型读取数据 功能 InputStream dataInputStream =new DataInputStream(bufferedInputStream); byte[] data = new byte[128]; while (dataInputStream.read(data) != -1) { //... } }}
5:实战例子(结束订单的积分抵扣)
1:正常的订单接口类(或者抽象类) 2:正常的订单实现类( 或者子类)3:装饰器类 传入订单实现类 或者子类) 对其同名方法做了增强4:调用时 可以使用 订单实现类(或者子类) 可以使用 订单实现类(或者子类)的 装饰器类
// 代理模式的代码结构(下面的接口也可以替换成抽象类)public interface OrderService {: void finishOrder(int amount);}
public class OrderServiceImpl implements OrderService { @Override public void finishOrder(int amount) { System.out.println("结束订单 扣除:"+amount); }}
public class OrderServiceImplDecorator implements OrderService { private OrderService orderService; public OrderServiceImplDecorator(OrderService orderService) { this.orderService = orderService; } @Override public void finishOrder(int amount) { //积分抵扣 amount = amount -5; orderService.finishOrder(amount); }}
public class OrderDecoratorDemoStart { public static void main(String[] args) { OrderService orderService =new OrderServiceImpl(); orderService.finishOrder(10); //对OrderService 的实现类的具体方法做了增强 OrderService orderServiceOther =new OrderServiceImpl(); OrderService orderServiceDecorator =new OrderServiceImplDecorator(orderServiceOther); orderServiceDecorator.finishOrder(10); }}
6:装饰器小结
装饰器模式主要解决继承关系过于复杂的问题,通过组合来替代继承。1:它可以可以对原始类嵌套使用多个装饰器。 注意:装饰器类需要跟原始类继承相同的抽象类或者实现相同接口类。2:它主要的作用是给原始类添加增强功能。 注意:装饰器类方法名 最好与原始类一致
项目连接
请配合项目代码食用效果更佳:项目地址:https://github.com/hesuijin/hesujin-design-patternGit下载地址:https://github.com.cnpmjs.org/hesuijin/hesujin-design-pattern.gitdemo-study模块 下 structure_design_pattern decorator包