创建型

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 来缓存已经创建好的享元对象,以达到复用的目的

 

行为型

1、观察者模式:

  • Observer Design Pattern

  • 定义:在对象之间定义一个一对多的依赖,当一个对象状态改变的时候,所有依赖的对象都会自动收到通知

  • 优点:降低了目标与观察者之间的耦合关系、目标与观察者之间建立了一套触发机制

  • 缺点:当观察者对象很多时,通知的发布会花费很长时间

  • 应用场景:对象间存在一对多关系,一个对象的状态发生改变会影响其他对象

  • 实际应用:邮件订阅、RSS Feeds、MQ 消息发布与订阅

 

2、模板模式:

  • Template Method Design Pattern

  • 定义:模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤

  • 实际应用:Java InputStream、Java AbstractList、Java Servlet、JUnit TestCase

  • 作用:代码复用(子类可以复用父类中提供的模板方法的代码)和扩展(提供功能扩展点,可以在不修改框架源码的情况下,基于扩展点定制化框架的功能)

  • 缺点:某种实现都需要子类,类的数量增多;代码可读性变差

 

3、策略模式:

  • Strategy Design Pattern

  • 定义:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户

  • 理解:包含策略的定义、实现类的创建、静态或者动态使用不同策略

  • 应用场景:替代根据类型进行多层 if -else 判断

  • 优点:避免使用多重条件语句;避免重复代码;提供相同行为的不同实现;符合开闭原则,灵活增加新算法

  • 缺点:策略实现类会比较多

 

4、职责链模式:

  • Chain of Responsibility Design Pattern

  • 作用:将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收对象能够处理它为止

  • 优点:将请求的发送者和接收者解耦;增强了系统的可扩展性,根据需要增加新的请求处理类,满足开闭原则;责任分担,每个类只需要处理自己该处理的工作,明确责任范围,符合单一职责原则

  • 缺点:不能保证每个请求一定被处理

  • 使用场景:可动态指定一组对象处理请求

  • 实际应用:Servlet Filter、Spring Interceptor、Dubbo Filter、 Netty ChannelPipeline

 

5、状态模式:

  • State Design Pattern

  • 定义:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为

  • 应用场景:当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为;一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态

  • 实际应用:状态机、游戏中不同人物角色的切换

  • 优点:封装状态变化、减少对象间的相互依赖、利于程序的扩展

  • 缺点:结构与实现都较为复杂

 

6、迭代器模式:

  • Iterator Design Pattern

  • 定义:提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示

  • 实际应用:JDK 中 Iterator 接口

  • 优点:封装集合内部的复杂数据结构,;将集合对象的遍历操作从集合类中拆分出来,职责单一;添加新的遍历算法更加容易,更符合开闭原则

 

7、访问者模式:

  • Visitor Design Pattern

  • 定义:允许一个或者多个操作应用到一组对象上,解耦操作和对象本身

  • 作用:在被访问的类里面加一个对外提供接待访问者的接口,解决稳定的数据结构和易变的操作耦合问题

  • 应用场景:对象结构相对稳定,但其操作算法经常变化;对象提供多种不同且不相关的操作,避免对结构的影响;

  • 优点:不修改对象结构,为对象结构中的元素添加新的功能,扩展性好;通过访问者来定义整个对象结构通用的功能,提高复用性;将数据结构与作用于结构上的操作解耦,灵活性好;访问者模式把相关的行为封装在一起,符合单一职责原则

  • 缺点:增加新的元素类很困难,违反开闭原则;具体元素对访问者公布细节,这破坏了对象的封装性;依赖了具体类,违反了依赖倒置原则;难理解、难实现

 

8、备忘录模式:

  • Memento Design Pattern

  • 定义:在不违背封装原则的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便之后恢复对象为先前的状态

  • 作用:提供了恢复状态的机制;实现了内部状态的封装,满足单一职责原则

  • 实际场景:游戏存档、编辑器和 IDE 的 ctrl+Z 回滚、数据库回滚

  • 缺点:内容过多或者过频繁备份,资源消耗大;适用场景单一

 

9、命令模式:

  • Command Design Pattern

  • 定义:将请求(命令)封装为一个对象,这样可以使用不同的请求参数化其他对象(将不同请求依赖注入到其他对象),并且能够支持请求(命令)的排队执行、记录日志、撤销等(附加控制)功能

  • 作用:将调用操作的对象与实现该操作的对象解耦;增加或删除命令非常方便,不影响其他类,符合开闭原则

  • 实际应用:游戏开发中将把请求包含的数据和处理逻辑封装为命令对象执行;与备忘录模式组合,实现 redo、undo 功能

  • 缺点:产生大量具体命令类

 

10、解释器模式:

  • Interpreter Design Pattern

  • 定义:为某个语言定义它的语法(或者叫文法)表示,并定义一个解释器用来处理这个语法

  • 作用:构建固定文法的句子的解释器

  • 缺点:通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢;较难调试;包含的文法规则很多时会引起类膨胀;适用场景单一

 

11、中介模式:

  • Mediator Design Pattern

  • 定义:定义了一个单独的(中介)对象,来封装一组对象之间的交互。将这组对象之间的交互委派给与中介对象交互,来避免对象之间的直接交互

  • 作用:引入中介这个中间层,将对象间的一对多关联转变为一对一的关联,只需要跟中介对象交互即可

  • 优点:降低了代码的复杂度,提高了代码的可读性和可维护性

  • 缺点:中介者的职责很大时,代码将复杂,可维护性降低


设计模式使用场景、优缺点汇总_java


建议点击 阅读原文 查看在线思维导图