目录与学习目标

  1. 1:从IO引入(奇怪的IO类)
  2. 2:基于继承的设计方案
  3. 3:基于装饰器模式的设计方案
  4. 4:实战例子(对IO类的各种增强)
  5. 5:实战例子(结束订单的积分抵扣)
  6. 6:装饰器小结

1:从IO引入(奇怪的IO类)

  1. Java IO 类库非常庞大和复杂,有几十个类,负责 IO 数据的读取和写入。
  2. 如果对 Java IO 类做一下分类,我们可以从下面两个维度将它划分为四类。具体如下所示:
字节流 字符流
输入流 InputStream Reader
输出流 OutputStream Writer
  1. 基于这4个父类上 扩展出很多子类

image.png

  1. 曾经对 Java IO 的一些用法产生过很大疑惑:
  2. 我们打开文件 test.txt,从中读取数据。
  3. 其中,InputStream 是一个抽象类,FileInputStream 是专门用来读取文件流的子类。
  4. BufferedInputStream 是一个支持带缓存功能的数据读取类,可以提高数据读取的效率。
  5. 需要先创建一个 FileInputStream 对象,然后再传递给 BufferedInputStream 对象来使用。
  6. Java IO 为什么不设计一个继承 FileInputStream 并且支持缓存的 BufferedFileInputStream
  1. InputStream inputStream = new FileInputStream("A:\\test.txt");
  2. InputStream bufferedInputStream = new BufferedInputStream(inputStream);
  3. byte[] data = new byte[128];
  4. while (bufferedInputStream.read(data) != -1) {
  5. //...
  6. }
  1. //继承 FileInputStream 并且支持缓存的 BufferedFileInputStream 类
  2. InputStream bufferedFileInputStream = new BufferedFileInputStream("A:\\test.txt");
  3. byte[] data = new byte[128];
  4. while (bufferedFileInputStream.read(data) != -1) {
  5. //...
  6. }

2:基于继承的设计方案

  1. 如果 InputStream 只有一个子类 FileInputStream 的话,那我们在 FileInputStream 基础之上,再设计一个孙子类 BufferedFileInputStream
  2. 也算是可以接受的,毕竟继承结构还算简单。
  3. 但实际上,继承 InputStream 的子类有很多。我们需要给每一个 InputStream 的子类,再继续派生支持缓存读取的子类。
  4. 除了支持缓存读取之外,如果我们还需要对功能进行其他方面的增强,
  5. 比如下面的 DataInputStream 类,支持按照基本数据类型(intbooleanlong 等)来读取数据。
  1. InputStream inputStream = new FileInputStream("A:\\test.txt");
  2. DataInputStream dataInputStream = new DataInputStream(inputStream);
  3. int data = dataInputStream.readInt();
  1. //继承 FileInputStream 并且支持基本数据类型的 dataFileInputStream 类
  2. InputStream dataFileInputStream = new DataFileInputStream("A:\\test.txt");
  3. int data = dataFileInputStream.readInt();
  1. 假如说这时候需要一个支持缓存、又支持按照基本类型读取数据的类,
  2. 那就要再继续派生出 BufferedDataFileInputStream
  3. 那么后面会变得没完没了。

3:基于装饰器模式的设计方案

  1. 之前还讲到“组合优于继承”,可以“使用组合来替代继承”。
  2. 针对刚刚的继承结构过于复杂的问题,我们可以通过将继承关系改为组合关系来解决。
  3. 下面的代码展示了 Java IO 的这种设计思路。
  4. 那装饰器模式就是简单的“用组合替代继承”吗?
  5. 当然不是。从 Java IO 的设计来看,装饰器模式相对于简单的组合关系,通常两个比较特殊的地方。
  6. 注意要有共同的接口类 或者 抽象类
  7. 1:第一个比较特殊的地方是:
  8. 装饰器类和原始类继承同样的父类,这样我们可以对原始类“嵌套”多个装饰器类。
  9. 我们对 FileInputStream 嵌套了两个装饰器类:
  10. BufferedInputStream DataInputStream,让它既支持缓存读取,又支持按照基本数据类型来读取数据。
  11. 第二个比较特殊的地方是:
  12. 装饰器类是对功能的增强,这也是装饰器模式应用场景的一个重要特点。
  13. 拿比较相似的代理模式和装饰器模式来对比:
  14. 代理模式中,代理类附加的是跟原始类无关的功能,
  15. 装饰器模式中,装饰器类附加的是跟原始类相关的增强功能。

4:实战例子(对IO类的各种增强)

  1. public class BufferedInputStream extends InputStream {
  2. protected volatile InputStream in;
  3. protected BufferedInputStream(InputStream in) {
  4. System.out.println("提供支持缓存读取数据 功能");
  5. this.in = in;
  6. }
  7. //...实现基于缓存的读数据接口...
  8. @Override
  9. public int read() throws IOException {
  10. return 0;
  11. }
  12. }
  13. public class DataInputStream extends InputStream {
  14. protected volatile InputStream in;
  15. protected DataInputStream(InputStream in) {
  16. System.out.println("提供基本类型读取数据 功能");
  17. this.in = in;
  18. }
  19. //...实现读取基本类型数据的接口
  20. @Override
  21. public int read() throws IOException {
  22. return 0;
  23. }
  24. }
  1. public class FileInputStreamDemo {
  2. public void FileInputStreamDemo() throws IOException {
  3. InputStream inputStream = new FileInputStream("A:\\test.txt");
  4. //BufferedInputStream 继承了 InputStream 提供支持缓存读取数据 功能
  5. InputStream bufferedInputStream = new BufferedInputStream(inputStream);
  6. //DataInputStream 继承了 InputStream 提供基本类型读取数据 功能
  7. InputStream dataInputStream =new DataInputStream(bufferedInputStream);
  8. byte[] data = new byte[128];
  9. while (dataInputStream.read(data) != -1) {
  10. //...
  11. }
  12. }
  13. }

5:实战例子(结束订单的积分抵扣)

  1. 1:正常的订单接口类(或者抽象类)
  2. 2:正常的订单实现类( 或者子类)
  3. 3:装饰器类 传入订单实现类 或者子类) 对其同名方法做了增强
  4. 4:调用时
  5. 可以使用 订单实现类(或者子类)
  6. 可以使用 订单实现类(或者子类)的 装饰器类
  1. // 代理模式的代码结构(下面的接口也可以替换成抽象类)
  2. public interface OrderService {:
  3. void finishOrder(int amount);
  4. }
  1. public class OrderServiceImpl implements OrderService {
  2. @Override
  3. public void finishOrder(int amount) {
  4. System.out.println("结束订单 扣除:"+amount);
  5. }
  6. }
  1. public class OrderServiceImplDecorator implements OrderService {
  2. private OrderService orderService;
  3. public OrderServiceImplDecorator(OrderService orderService) {
  4. this.orderService = orderService;
  5. }
  6. @Override
  7. public void finishOrder(int amount) {
  8. //积分抵扣
  9. amount = amount -5;
  10. orderService.finishOrder(amount);
  11. }
  12. }
  1. public class OrderDecoratorDemoStart {
  2. public static void main(String[] args) {
  3. OrderService orderService =new OrderServiceImpl();
  4. orderService.finishOrder(10);
  5. //对OrderService 的实现类的具体方法做了增强
  6. OrderService orderServiceOther =new OrderServiceImpl();
  7. OrderService orderServiceDecorator =new OrderServiceImplDecorator(orderServiceOther);
  8. orderServiceDecorator.finishOrder(10);
  9. }
  10. }

6:装饰器小结

  1. 装饰器模式主要解决继承关系过于复杂的问题,通过组合来替代继承。
  2. 1:它可以可以对原始类嵌套使用多个装饰器。
  3. 注意:装饰器类需要跟原始类继承相同的抽象类或者实现相同接口类。
  4. 2:它主要的作用是给原始类添加增强功能。
  5. 注意:装饰器类方法名 最好与原始类一致

项目连接

  1. 请配合项目代码食用效果更佳:
  2. 项目地址:
  3. https://github.com/hesuijin/hesujin-design-pattern
  4. Git下载地址:
  5. https://github.com.cnpmjs.org/hesuijin/hesujin-design-pattern.git
  6. demo-study模块 structure_design_pattern decorator