秒懂设计模式--学习笔记(9)【结构型-装饰器模式】

8、装饰器模式

8.1 装饰器模式(Decorator)
  • 能够在运行时动态地为原始对象增加一些额外的功能
  • 装饰器非常类似于“继承”,它们都是为了增强原始对象的功能,区别在于方式的不同
    • 后者是在编译时(compile-time)静态地通过对原始类的继承完成
    • 前者则是在程序运行时(run-time)通过对原始对象动态地“包装”完成,是对类实例(对象)“装饰”的结果
  • 测试类结构
    装饰器测试类结构
8.2 装修(举例)
  • 不改变其原始结构的前提下使客体功能得到扩展、增强
  • 以室内装潢为例
    • 装修风格多种多样
    • 朴素的毛坯房能给业主留有更大的装修选择空间,以根据自己的喜好进行二次加工
    • 成品一定是由半成品加工而成的
    • 灵活多变的装饰才会带来更多的可能,因此装饰器模式应运而生
8.3 化妆(示例)
  • 首先对于任何妆容展示者必然对应一个标准的展示行为show()
  • 我们将它抽象出来定义为接口Showable
  • Showable这个标准行为需要人去实现,定义女生类,素颜展示
    • 如果客户端直接调用show()方法,就会出现素面朝天的结果
    	package decorator;
    
    	/**
    	 * 可展示者
    	 **/
    	public interface Showable {
    	    /**
    	     * 标准展示行为
    	     */
    	    public void show();
    	}
    
    • 化妆品我们在这里称为“装饰器”: 化妆品装饰器类Decorator
    	package decorator.base;
    	
    	import decorator.Showable;
    	
    	/**
    	 * 化妆品装饰器类
    	 **/
    	public class Decorator implements Showable {
    	    /**
    	     *被装饰的展示者
    	     */
    	    Showable showable;
    	
    	    /**
    	     * 构造时注入被装饰者
    	     * @param showable
    	     */
    	    public Decorator(Showable showable) {
    	        this.showable = showable;
    	    }
    	
    	    @Override
    	    public void show() {
    	        //化妆品粉饰开始
    	        System.out.print("粉饰【");
    	        //被装饰者的原生展示方法
    	        showable.show();
    	        //粉饰结束
    	        System.out.print("】");
    	    }
    	}
    
    • 装饰器实现类
    	package decorator.entity;
    	import decorator.Showable;
    	/**
    	 * 装饰器实现类
    	 **/
    	public class Girl implements Showable {
    	    @Override
    	    public void show() {
    	        System.out.print("女生的素颜");
    	    }
    	}
    
    • 测试类
    	package decorator.base;
    	import decorator.entity.Girl;		
    	/**
    	 * 客户端测试类
    	 **/
    	public class Client {
    	    public static void main(String[] args) {
    	        //用装饰器装饰后后再展示
    	        new Decorator(new Girl()).show();
    	    }
    	}
    
8.4 化妆品的多样化
  • 化妆品的多样性决定了装饰器应该是多态化的
    • 单个装饰器应该只负责自己的化妆功效
    • 把化妆品按功能分类才能让用户更加灵活地自由搭配,用哪个或不用哪个由用户自己决定,而不是把所有功能都固化在同一个装饰器里
    • 如果让所有化妆品类都实现Showable接口,每个化妆品类里都要引用这个被装饰者,这显然会导致代码冗余。
    • Showable接口是能够满足多态化需求的,但它只是对行为接口的一种规范,极度的抽象并不具备对代码继承的功能
  • 化妆品的多态化还需要接口与抽象类的搭配使用才能两全其美,装饰器类的抽象化势在必行
    • 我们将化妆品装饰器类修改为装饰器抽象类,这主要是为了不允许用户直接实例化此类
    • 调用了被装饰者的show()方法,而不再做任何装饰操作
    • 至于具体如何装饰则属于其子类的某个化妆品类的操作范畴了
    • 抽象装饰器DecoratorAbstract
    	package decorator.ab;
    	
    	import decorator.Showable;
    	
    	/**
    	 * 化妆品装饰器类: 抽象类
    	 **/
    	public abstract class DecoratorAbstract implements Showable {
    	    protected Showable showable;
    	
    	    public DecoratorAbstract(Showable showable) {
    	        this.showable = showable;
    	    }
    	
    	    @Override
    	    public void show() {
    	        // 直接调用不加任何装饰
    	        showable.show();
    	    }
    	}
    
    
    • 实现类
      • 粉底类FoundationMakeup、口红类Lipstick
      package decorator.ab;
      import decorator.Showable;
      /**
       * 粉底类
       **/
      public class FoundationMakeup extends DecoratorAbstract {
      
          /**
           * 调用抽象父类的构造注入
           * @param showable
           */
          public FoundationMakeup(Showable showable) {
              super(showable);
          }
      
          @Override
          public void show() {
              System.out.print("打粉底【");
              showable.show();
              System.out.print("】");
          }
      }
      
      package decorator.ab;
      
      import decorator.Showable;
      
      /**
       * 口红类
       **/
      public class Lipstick extends DecoratorAbstract {
          public Lipstick(Showable showable) {
              super(showable);
          }
      
          @Override
          public void show() {
              System.out.print("涂口红【");
              showable.show();
              System.out.print("】");
          }
      }
      
      • 不用去实现Showable接口了,而是继承了装饰器抽象类,如此父类中对被装饰者的定义得以继承
      • 重写show()方法, 加上自己特有的操作
    • 客户端类Client
      • 涂口红【打粉底【女生的脸庞】】
      • 需要注意的是一系列构造产生的顺序
 package decorator.ab;

import decorator.entity.Girl;
import decorator.Showable;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.zip.ZipInputStream;

/**
 * 客户端测试类
 **/
public class Client {

    public static void main(String[] args) throws FileNotFoundException {
        test1();
        test2();
    }
    
    private static void test1() {
        Girl girl = new Girl();
        /**
         * 上粉底
         */
        Showable foundationMakeup = new FoundationMakeup(girl);
        /**
         * 涂口红
         */
        Showable medeupGirl = new Lipstick(foundationMakeup);
        medeupGirl.show();
    }
    
    private static void test2() throws FileNotFoundException {
        // 1.首先以文件file初始化
        File file = new File("/压缩包.zip");
        // 4.最外层再用压缩包输入流ZipInputStream进行最终装饰,使文件输入流具备Zip格式文件的功能
        ZipInputStream zipInputStream = new ZipInputStream(
                // 3.外层用缓冲输入流BufferedInputStream进行装饰,使文件输入流具备内存缓冲的功能
                new BufferedInputStream(
                        // 2.构造文件输入流FileInputStream
                        new FileInputStream(file)
                )
        );
    }
}
8.5 装饰器
  • 如同“俄罗斯套娃”一般层层嵌套
  • 其实装饰器模式在Java开发工具包(Java Development Kit,JDK)里就有大量应用
    • 例如“java.io”包里一系列的流处理类InputStream、FileInputStream、BufferedInputStream、ZipInputStream等
  • 举个例子,当对压缩文件进行解压操作时,我们就会用构造器嵌套结构进行文件流装饰,如:ab.Client.test2()
8.6 自由嵌套
  • 装饰器模式最终的目的就在于“装饰”对象
  • 其中装饰器抽象类扮演着至关重要的角色,它实现了组件的通用接口
  • 并且使自身抽象化以迫使子类继承,使装饰器固定特性的延续与多态化成为可能
8.7 装饰器模式的各角色定义
  • Component(组件接口):
    • 所有被装饰组件及装饰器对应的接口标准指定进行装饰的行为方法。如:展示接口Showable。
  • ConcreteComponent(组件实现):
    • 需要被装饰的组件实现组件接口标准,只具备自身未被装饰的原始特性。如:类Girl
  • Decorator(装饰器):
    • 装饰器的高层抽象类,
    • 同样实现组件接口标准且包含一个被装饰的组件
  • ConcreteDecorator(装饰器实现)
    • 继承自装饰器抽象类的具体子类装饰器,可以有多种实现,在被装饰组件对象的基础上为其添加新的特性
    • 如: 粉底类FoundationMakeup、口红类Lipstick
8.8 装饰器模式
  • 装饰器模式可以将不同功能的单个模块规划至不同的装饰器类中
  • 客户端可以根据自己的需求自由搭配各种装饰器,每加一层装饰就会有新的特性体现出来
  • 最终使原始对象的特性动态地得到增强

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-10 18:46:02       51 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-10 18:46:02       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-10 18:46:02       44 阅读
  4. Python语言-面向对象

    2024-07-10 18:46:02       55 阅读

热门阅读

  1. 完全背包求具体方案(c++题解)

    2024-07-10 18:46:02       21 阅读
  2. Pull Request

    2024-07-10 18:46:02       18 阅读
  3. stm32使用硬件SPI

    2024-07-10 18:46:02       16 阅读
  4. Elasticsearch7.10集群搭建

    2024-07-10 18:46:02       16 阅读
  5. 串口工具推荐

    2024-07-10 18:46:02       19 阅读
  6. Python实现Mybatis Plus

    2024-07-10 18:46:02       23 阅读
  7. 管理客户的10个CRM系统技巧

    2024-07-10 18:46:02       23 阅读
  8. 四、Python日志系统之日志文件的备份和删除

    2024-07-10 18:46:02       17 阅读
  9. ZC2205-24V500mAUltralow-Quiescent-Current LDO

    2024-07-10 18:46:02       12 阅读
  10. 六、golang基础之面向对象特征

    2024-07-10 18:46:02       18 阅读