设计模式

2020-07-06

创建型

1、单例模式:

  • Singleton Design Pattern
  • 一个类只允许创建一个对象(或者实例),这个类就是一个单例类
  • 单例的实现:饿汉式、懒汉式、双重检查、静态内部类、枚举
  • 作用:避免频繁创建和销毁系统全局使用的对象
  • 应用场景:全局唯一类,如 系统配置类、系统硬件资源访问类;公共服务类:如 servlet、springmvc 中的 bean、数据库连接池;Web 计数器、序列号生成器
  • 缺陷:对 OOP 特性的支持不友好、隐藏类之间的依赖关系、对代码的扩展性不友好、对代码的可测试性不友好、不支持有参数的构造函数
  • 替代方案:程序员控制、工厂模式、IOC 容器
  • 扩展:线程纬度的单例、集群环境下的单例、多例模式

 

2、工厂模式:

  • Factory Design Pattern
  • 分为:简单工厂、工厂方法和抽象工厂。简单工厂可以看作是工厂方法的一种特例
  • 作用:封装变化(创建逻辑有可能变化,封装成工厂类之后,创建逻辑的变更对调用者透明)、代码复用(创建代码抽离到独立的工厂类之后可以复用)、隔离复杂性(封装复杂的创建逻辑,调用者无需了解如何创建对象)、控制复杂度(将创建代码抽离出来,让原本的函数或类职责更单一,代码更简洁)
  • 简单工厂适用场景:对象的创建逻辑比较简单,if-else 并不多
  • 工厂方法适用场景:对象的创建逻辑比较复杂,不只是简单的 new 一下就可以,而是要组合其他类对象,做各种初始化操作;避免 if-else
  • 抽象工厂适用场景:可以创建多种不同类型的工厂,能够创建一族对象,减少了工厂类
  • 简单工厂、工厂方法、抽象工厂之间的关系与选择:如果实例对象的创建逻辑并发不复杂、实例对象的种类并不多即 if 分支不多,且基本不会有太大变动,使用简单工厂模式;需要创建的实例化对象种类较多、变动可能性大、对象的创建逻辑比较复杂,使用工厂方法,可以去除 if 多分支、将复杂的对象创建逻辑封装到每个具体工厂中;抽象工厂的使用并不多,适用于实例对象的层级逻辑比较复杂,抽象出多个实例对象组,每个工厂创建一组实例对象

 

3、建造者:

  • Builder Design Pattern
  • 应用场景:能够解决构造方法的参数很多传错参数的问题;可以保证类属性字段安全,不用提供 public setter 方法;可以进行属性字段间的校验、计算、填充等
  • 缺点:创建对象前必须创建 Builder 对象,多一些性能开销,对性能要求极高的的场景下慎用;代码有点啰嗦

 

4、原型模式:

  • Prototype Design Pattern
  • 作用:对象的创建成本比较大,同一个类的不同对象之间的大部分字段相同,可以对已有对象进行拷贝的方式创建新对象,达到节省新对象的创建时间的目的
  • 方式:浅拷贝(仅复制基本类型、引用类型的属性变量、引用类型变量的指针;并不复制引用类型变量指向的堆内存中的对象,而是指向同一个对象)、深拷贝(复制基本类型、应用类型的属性变量、引用类型变量的指针、引用类型指向的堆内存中对象,获得一个完全独立的对象)
  • 实现:浅拷贝(实现 Cloneable 接口,重写 clone 方法)、深拷贝(实现 Cloneable 接口,递归 clone 引用对象或 new 新对象;借助序列化完成深拷贝)

 

结构型

1、代理模式:

  • Proxy Design Pattern
  • 作用:在不改变原始类(或叫被代理类)代码的情况下,通过引入代理类来给原始类附加功能。使用代理对象完成用户请求,屏蔽用户对真实对象的访问
  • 方式:静态代理、动态代理(JDK 自带、cglib、Javassist 可实现)
  • 应用场景:代理模式常用在业务系统中开发一些非功能性需求,比如:监控、统计、鉴权、限流、事务、幂等、日志。将这些附加功能与业务功能解耦,放到代理类统一处理,程序员只需要关注业务方面的开发;远程代理、虚拟代理、安全代理...;框架开发,如 Spring AOP 功能(JDK动态代理、cglib)
  • 缺点与注意:静态代理需要一个代理类对应一个被代理类,容易造成类文件增加很多,增加大量的维护工作;JDK 动态代理,需要满足一个条件,就是被代理类需要实现接口,否则会抛出 java.lang.ClassCastException 异常;随着 JVM 的优化,JDK 1.8 开始 JDK 动态代理的性能赶上并超越 cglib;cglib 采用动态创建子类的方法,final 修饰的方法无法进行代理;cglib 创建代理对象时所花费的时间却比 JDK 动态代理要多

 

2、桥接模式:

  • Bridge Design Pattern
  • 作用:将抽象和实现解耦,让它们可以独立变化;让一个类拥有两个或多个独立变化的维度,通过组合的方式,让这两个或多个维度可以独立进行扩展
  • 实际应用:JDBC 驱动程序的加载、JVM 在多操作系统间的实现、GUI 在多操作系统间的实现
  • 优点:分离抽象接口与实现、类似多继承但比多继承灵活、可扩展性强、功能对用户透明,隐藏了实现细节
  • 缺点:需要抽象出变化的维度,对业务理解程度要求较高、对系统的设计能力要求较高、代码较难理解

 

3、装饰器模式:

  • Decorator Design Pattern
  • 作用:能够动态地给一个对象添加额外的职责,灵活地生成子类;主要解决了原始类在多个方向上功能扩展的问题;原始类可以嵌套使用多个装饰器类,装饰器类需要跟原始类继承相同的抽象类或实现相同的接口;装饰器类具有 is-a 和 has-a 的双重关系
  • 角色:被修饰对象的基类、具体被装饰的类、装饰者的抽象类、具体的装饰类
  • 优点:可以动态扩展一个实现类的功能,装饰类和被装饰类可以独立扩展,不会相互耦合
  • 缺点:层次关系比较复杂

 

4、适配器模式:

  • Adapter Design Pattern
  • 作用:适配,将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作
  • 应用场景:对有缺陷的接口设计进行再次封装、统一多个类的接口设计、替换依赖的系统、兼容老版本接口、适配不同格式的数据
  • 实际例子:JDK Iterator 适配 Enumeration;slf4j 对不同的日志框架进行了适配
  • 优点:适配器模式是一种补充模式,可以用来系统的后期扩展与修改
  • 缺点:不适合过多使用,否则代码中的方法调用比较乱

 

5、门面模式:

  • Facade Design Pattern
  • 门面模式为子系统提供一组统一的接口,定义一组高层接口让子系统更易用
  • 作用:封装系统的底层实现,隐藏系统的复杂性,提供一组更加简单易用、更高层的接口;客户端原本对多个子系统接口的调用,改为调用门面的一个接口,较少交互,提升性能;解决事务问题

 

6、组合模式:

  • Composite Design Pattern
  • 作用:将一组对象组织成树状结构,以表示部分和整体的层次结构,让客户端可以统一单个对象和组合对象的逻辑;更像是一种树形结构数据场景下的数据结构与算法的抽象
  • 使用场景:主要用来处理树形结构的数据。如部门、人员组成树状结构,计算薪资
  • 优点:灵活增加节点、调用简单、代码简洁
  • 缺点:适用场景非常单一

 

7、享元模式:

  • Flyweight Design Pattern
  • 作用:复用对象,节省内存。(前提是享元对象是不可变对象)
  • 应用场景:当一个系统中存在大量重复对象的时候,可以利用享元模式,将对象设计成享元,在内存中只保留一份实例,供多处代码引用,减少内存中对象的数量,节省内存。如JDK 中 Integer 类就使用了享元模式缓存了 -128 ~ 127 范围的整数
  • 实现:主要是通过工厂模式,在工厂类中,通过一个 Map 或者 List 来缓存已经创建好的享元对象,以达到复用的目的

 

ConstXiong 备案号:苏ICP备16009629号-3