# design_pattern **Repository Path**: wukong-yu/design_pattern ## Basic Information - **Project Name**: design_pattern - **Description**: 学习java设计模式,在源码中学习思维,在设计模式中学习源码 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: developer - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-06-08 - **Last Updated**: 2022-08-26 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 参考 [软件设计模式概述](http://c.biancheng.net/view/1343.html) ## 设计默认原则 - 单一职责 - 接口隔离: segregation - 依赖倒置: inversion - 里氏替换: - 开闭原则: ```angular2html 1. 高层模块不应该依赖低层模块,二者应该依赖其抽象 2. 依赖倒置的思想是面向接口编程 3. 依赖倒置原则:以抽象为基础搭建的建构比以细节为基础的架构要稳定 4. 使用接口或者抽象类的目的是指定规范,而不涉及任何具体操作,细节任务由实现类完成 ``` ### 迪米特法则 ```angular2html 迪米特法则(Law of Demeter) 定义: 如果两个软件实体之间无须直接通信,那么就不应该发生直接的互相调用,可以通过第三方转发调用。 其目的是降低类之间的耦合度,提高模块的相对独立性。 ``` - 迪米特法则(定义1): 最少知识原则,就是一个类对自己依赖的类知道越少越好。对于被依赖的类来说,无论逻辑多么复杂, 都尽量封装在类的内部,对外除了public方法,不对外泄露任何信息; - 迪米特法则(定义2): 只与直接朋友通信。称出现成员变量、方法参数、方法返回值的类都为直接朋友, 而局部变量不是直接朋友。陌生类最好不要作为局部变量出现在类的内部。 ### UML关联关系 - 依赖: 虚线+箭头 - 关联: 单向关联、双向关联; 实线+箭头 - 泛化(继承): 实线+空心三角 - 实现: 虚线+空心三角 - 组合: 整体和部分不可分开; 实菱形+实线+箭头 - 聚合: 整体和部分可以分开; 空菱形+实现+箭头 ## 创建型模式(4种) - 单例模式(singleton) ```angular2html 饿汉模式: 静态常量 优势: 1. 代码实现简单,在类加载时实例化对象,避免多线程同步问题; 缺点: 1. 在类加载时就实例化对象,没有达到懒加载的效果,可能会造成系统内存浪费; 2. 可能造成类加载问题;(参考JVM) 懒汉模式: 一般写法: 问题: 存在线程安全问题 同步机制(同步方法): synchronized 关键字,通过在方法上添加同步关键字; 优势: 1. 解决线程安全问题 缺点: 1. 效率太低,由于同步关键字在方法上,每次调用 getInstance() 都会被同步执行,同步范围过大导致效率低下 同步机制(同步代码块): 在代码块中添加同步关键字,存在线程安全问题 com.chinaunicom.create.singleton.hungry.HSingleton03@49fb8056 com.chinaunicom.create.singleton.hungry.HSingleton03@9d30fed 双重校验锁: 采用两次检查来判断 instance 是否为空进行线程安全校验; 优势: 1. 线程安全 2. 延迟加载 3. 效率较高 静态内部类(加载过程参考JVM): 特点: 在加载外部类时,内部类并不会立即加载; 在调用getInstance()方法获取内部类对象时, 才会加载 SingletonInstance 类以及初始化静态成员,只创建一次; (线程安全由 JVM保证 ) 静态内部类: 1. 静态内部类中,既可以定义成员变量、成员方法、构造方法、构造代码块以及静态方法、静态成员变量等; 2. 静态内部类中,既可以调用外部类的静态方法、静态成员变量,但是不能调用外部类成员变量、成员方法等; 枚举(JDK自带): ``` - 工厂模式(factory) ```angular2html 简单工厂模式: 说明: 角色(3): 简单工厂 (负责实现创建所有实例的内部逻辑)、抽象产品、具体产品; 应用: Calendar(JDK: Calendar.getInstance())、 工厂方法模式: 说明: 方法下沉到子类 角色(4): 抽象工厂、具体工厂、抽象产品、具体产品; 应用: Logback 抽象工厂模式: 角色(4): 抽象工厂、具体工厂、抽象产品、具体产品; (同工厂方法角色一致) 说明: 抽象工厂是简单工厂+ 工厂方法的结合实现; 一个抽象工厂可以生产出多个产品,而工厂方法模式中每个工厂类只能生产出一个产品; 应用: Connection(java.sql.*) ``` - 原型模式(Prototype) ```angular2html 浅拷贝: 角色(3): 抽象原型类(实现Cloneable类)、具体原型类、访问类 说明: 创建一个拷贝对象,新对象和原对象的属性内容一致; 对于基本类型数据,新对象和原对象的内容hashCode一致; 对于非基本类型,新对象成员属性的指针指向原对象成员属性指向的内存地址; 应用: 1. Bean配置(Spring源码: scope属性): [https://blog.csdn.net/FanYien/article/details/117898188](spring源码中prototype属性) 优势: 1. 实现简单; 2. 简化创建对象的流程,提高创建效率 缺点: 1. 当原对象中存在引用对象成员变量时,进行浅拷贝之后,对原对象中成员变量对象的属性进行修改,则出现拷贝对象中的引用对象的成员信息也被修改; 原因: 拷贝对象的引用成员属性的指向原对象的引用对象所指向的内存地址,即原对象和拷贝对象属性中的引用对象都指向同一块内存地址; 修改原对象属性的引用对象的内容,并不会修改其所指向的内存地址,所以拷贝对象的属性信息也被修改; 深拷贝: 角色(3): 抽象原型类(实现Clonable类并重写clone方法)、具体原型类、访问类 说明: 深拷贝相对浅拷贝来说,深拷贝需要手动重写 clone()方法。 如果被克隆对象中引用其他对象时,则需要手动调用该引用对象的clone()重写方法来重新生成一个新的对象; 区别: 深拷贝copy的是值,浅拷贝是引用; 问题: 1. 克隆是否会破坏单例模式 ? 如果被克隆的对象是单例模式,那么深拷贝就会破坏单例。实际上防止克隆破坏单例解决方法是: 禁止深克隆; ``` - 建造者模式(Builder) ```angular2html 角色(4): 1. 抽象建造者Builder (创建每个部件的抽象方法的接口)、 2. 具体建造者、 3. 指挥者 (聚合一个Builder接口对象,主要作用有: 1.隔离了客户与对象的生产过程; 2.负责控制产品的生产过程) 说明: 应用: Appendable(JDK源码, Appendable、StringBuilder、AbstractStringBuilder) ``` ## 结构型模式(7种) - 适配器模式 ```angular2html 角色(3): 1. 目标: 系统业务接口,抽象类或者接口; 2. 适配者: 被访问和适配的组件库中的组件接口; 3. 适配器: 1. 转换器,通过继承或者引用适配者对象,将适配者接口转成目标接口; 2. 适配器实现目标对象接口; 说明: 将一个接口转成另一个接口,使得原本由于接口不能兼容而不能一起工作的类进行工作; 分为: 类适配器模式、对象适配器模式; 应用: HandlerAdapter(springmvc中接口请求过程) 优势: 1. 复用了现存的类,减小修改原有类,进而复用适配者类; 2. 实现 适配者和 目标 类之间的解耦; 缺点: 1. 增加代码复杂度,降低代码可读性,过多使用代码适配器模式使得代码凌乱; 类适配器模式(classmodel): 对象适配器模式(objectmodel): 接口适配器模式(interfacemodel): 双向适配器模式(twoway): ``` - 桥接模式 ```angular2html 说明: 将抽象和实现分离,使得可以独立变化。用组合关系来替代继承关系,来降低抽象和实现这两个维度的耦合度; 当一个类内部具备两种或多种变化维度时,使用桥接模式可以解耦这些变化的维度,使得高层结构稳定。 使用场景: 1. 当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时; 2. 当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时; 3. 当一个系统需要在构建抽象化角色和具体化角色之间增加更多的灵活性时; 角色: 1. 抽象化角色(抽象类,并包含一个对实例化的实例)、 2. 扩展抽象化角色(实现父类方法,通过组合关系调用实现化角色中的业务方法)、 3. 实现化角色(定义接口供扩展抽象化角色调用)、 4. 具体实现化角色 优点: 1. 抽象和实现分离,扩展能力强 2. 符合开闭原则 3. 符合合成复用原则 4. 实现细节对客户透明 缺点: 1. 由于聚合关系发生在抽象层,要求开发者针对抽象化进行设计和编程,能正确识别出系统这两个独立变化的维度,增加系统的理解和设计难度 应用: 1. JDBC源码分析(java.sql.Connection及其实现为具体实现角色、java.sql.DriverManager为抽象类角色) ``` - 装饰者模式 ```angular2html 说明: 1. 在不改变现有对象结构的情况下,动态的给对象增加一些功能的模式 2. 装饰者模式的uml 类似 适配器的uml 使用场景: 1. 对现有一组基本功能进行排列组合而产生非常多的功能时,采用继承关系难以实现; 2. 当对象的功能要求可以动态的添加,也可以动态的撤销时; 角色: 1.抽象构件角色: 定义抽象接口 2.具体构件角色: 实现抽象构件 3.抽象装饰角色: 继承抽象构件,并包含具体构件的实例 4.具体装饰角色: 实现抽象装饰的相关方法 优势: 1. 装饰器是继承的有力补充,即插即用; 2. 通过使用不同的装饰类以及这些类的排列组合,可以实现不同的效果(类似: 桥接模式) 3. 装饰者模式符合开闭原则 缺点: 装饰者模式会增加很多子类,过度使用会增加程序的复杂度; 应用: I/O库 ``` - 组合模式 ```angular2html 说明: 将对象组合成树形结构以表示"部份-整体"的层次结构,使得用户对单个对象和组合对象得使用具有一致性; 组合模式经常用于树形结构。 使用场景: 1. 需要实现树状对象结构,可以使用组合模式; 2. 客户端以相同方式处理简单和复杂元素时,可以使用该模式; 角色: 1. 抽象组合角色(定义公共抽象方法) 2. 具体组合角色1 3. 具体组合角色2(内部包含一个 抽象组合的实例化对象) 优势: 1. 利用多态和递归机制方便得使用复杂树结构; 2. 符合开闭原则,无需更改现有代码即可添加新元素; 缺点: 1. 对于功能差异较大的类,提供公共接口有点困难。在特定情况下,需要过渡一般化组件接口,使得令人难以理解; 应用: ``` - 外观模式 ```angular2html 说明: 结构型设计模式,可以为程序库、框架以及复杂类提供一个简单的入口; 使用场景: 1. 子系统复杂的系统; 2. 内部层次结构复杂的系统; 角色: 1. 外观角色: 封装原有资源类,用户通过调用外观类的方法,来实现子类资源类提供的功能; 2. 子系统角色: 程序库、框架以及复杂类等 3. 用户角色 优势: 1. 简化调用: 用户使用时方便,只需要调用外观类方法即可,无需关注资源类中功能的具体实现; 2. 降低耦合度: 降低用户端与资源类之间的耦合度; 3. 层次控制: 层次结构复杂的系统,有些方法需要提供给系统外部使用,有的方法需要在内部使用,将提供给外部的功能定义在外观类中,这样方便维护; 4. 符合迪米特法则: 最少知道原则,用户不需要了解系统内部情况,用户不需要了解子系统内部情况,只与外观类交互; 缺点: 1. 不符合开闭原则: 扩展子系统时不符合开闭原则 应用: 1. spring-jdbc中: org.springframework.jdbc.support.JdbcUtils 2. tomcat 中大量使用外观模式,例: RequestFacade、SchemaGeneratorFacade; ``` - 代理模式 ```angular2html 静态代理: 说明: 为对象提供一个替身,以控制对这个对象的访问,即通过代理对象访问目标对象。 角色: 1. 代理对象角色: 2. 被代理对象角色: 优点: 1. 符合开闭原则:在不修改目标对象功能的前提下,通过代理对象对目标功能进行扩展; 缺点: 1. 代码将会变得复杂,因为需要创建很多类; 动态代理: jdk动态代理: 说明: 基于 java.lang.reflect.Proxy 类创建动态代理对象; 通过实现 java.lang.reflect.InvocationHandler 接口重写 invoke()方法,自定义目标方法回调的内部逻辑实现; 条件: 1. 目标对象必须实现接口类; 接口信息: /** * 创建代理对象 * ClassLoader loader : 当前目标对象的类加载器 * Class[] interfaces : 当前目标对象所实现的接口类型 * InvocationHandler h : 回调处理器,当执行目标方法时,会回调处理器中的方法; */ public Object newProxyInstance(); cglib动态代理: 说明: Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展。 基于 net.sf.cglib.proxy.Enhancer 类创建动态代理对象(即增强类); 通过实现 net.sf.cglib.proxy.MethodInterceptor 接口重写 intercept()方法,自定义目标方法回调的内部逻辑实现; 条件: 1. 目标对象可以为任意类 总结: 目标对象需要实现接口,用JDK代理 目标对象不需要实现接口,用Cglib代理 ``` - 享元模式 ```angular2html 说明: 避免在所有对象中保存所有数据的方式,通过共享所有对象共有的相同状态,能在容器中保存更多的对象; 角色: 1. 内部角色: 即所有对象所共有的部分; 2. 外部角色: 每个对象所特有的部分; 使用场景: 1. 程序需要生成数量巨大的相似对象 2. 这将耗尽目标设备的所有内存 3. 对象中包含可抽取且能在多个对象间共享的重复状态。 优势: 1. 降低内存空间的使用; 缺点: 1. 需要牺牲执行速度来换取内存 2. 代码会变得更加复杂。 应用: ``` ## 行为型模式() - 职责链模式(responsibilitychain) ```angular2html 参考SpringMVC应用框架中 DispatcherServlet职责链模式 ``` - 命令模式 ```angular2html 说明: 是一种数据驱动的设计模式。请求以命令的形式包裹在对象中,并传递给调用对象。 角色: 1. 调用者角色 (Application) 2. 命令角色(Command) 3. 接受者角色(Edit) 优点: 1. 符合单一职责原则 2. 符合开闭原则; 缺点: 1. 可能增加代码复杂性; 使用场景: 应用: ``` - 中介者模式 ```angular2html 说明: 该模式会限制对象之间的直接交互,迫使他们通过一个中介者对象进行合作,减少对象之间混乱无需的依赖关系; 角色: 1. 抽象中介者角色(Mediator) 2. 具体中介者角色 (ConcreteMediator) 3. 调用者角色 (Component / ComponentA / ComponentB ) 优点: 1. 符合单一职责原则 2. 符合开闭原则 3. 降低组件之间的耦合度 4. 方便复用各个组件 缺点: 1. 可能需要了解过多的对象,或负责过多的对象 使用场景: 1. 对象之间存在复杂的引用关系,系统结构复杂且难以理解 2. 一个对象引用其他很多对象并直接通信,导致这个对象难以复用 3. 需要通过一个中间类来封装多个类中的行为,有不想生成太多的子类; 应用: 1. 在 MVC中, Controller是 View 和 Model 之间的中介者; 2. ORM (对象-关系映射中) 中将 ResultSet 转成 JavaBean; ``` - 迭代器模式 ```angular2html 说明: 能在不暴露集合底层表现形式的情况下遍历集合中所有元素; 角色: 1. 抽象迭代器角色 2. 具体迭代器角色 3. 抽象业务角色 4. 具体业务角色 优点: 1. 符合单一职责原则 2. 符合开闭原则 3. 缺点: 1. 如果是简单的集合进行交互,该模式会矫枉过正; 2. 对某些集合,使用迭代器效率低; 使用情景: 应用: 1. JDK中Iterator自带的迭代器 ``` - 备忘录 ```angular2html 说明: 在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以把该对象恢复到原先的状态; 角色: 1. 原发器角色(Originator): 负责操作备忘录数据,记录当前时刻的内部状态,负责创建和恢复备忘录数据; 2. 备忘录角色(Memento): 负责存储发起人对象的内部状态,在进行恢复时提供给发起人需要的状态; 3. 负责人角色(Caretaker): 负责保存备忘录对象; 优点: 1. 系统中如果存在某个错误操作而破坏了数据的完整性,那么可以使用备忘录模式; 2. 符合单一职责原则; 缺点: 1. 在实际系统中,可能需要维护多个备份数据,需要额外的资源; 使用情景: 1. 如果系统需要提供回滚操作时,使用备忘录模式就非常合适; 应用: 1. Spring-webflow源码(StateManageableMessageContext) ``` - 观察者模式 ```angular2html 说明: 允许设计一种订阅模式,可在对象事件发生时通知多个 "观察者" 该对象的其他对象。 角色: 1. 发布者角色: 2. 抽象订阅者角色: 3. 具体订阅者角色: 优点: 1. 观察者和被观察者是抽象耦合,只有在运行时才建立对象之间的联系; 缺点: 1. 如果被观察者有很多观察者的话,将通知所有被观察者会花费很多时间; 2. 如果被观察者和观察者之间存在循环依赖,则会导致系统崩溃; 使用情景: 1. 一个对象必须通知其他对象,而并不知道这些对象是谁; 2. 一个对象的改变将导致其他一个或多个对象也发生变化,可以降低对象之间的耦合度; 应用: 1. spring源码(ContextLoaderListener、ServletContextEvent) ``` - 状态模式 ```angular2html 说明: 类的行为是基于它的状态改变的,我们创建表示各种状态的对象和一个行为随着状态对象的改变而改变的content对象; 角色: 1. 抽象状态角色: 该接口声明特定状态的方法; 2. 具体状态角色: 会自行实现特定于状态的方法; 3. 上下文角色: 保存了对于一个具体状态对象的引用,并会将所有与该状态有关的工作委派给它。上下文通过状态接口与状态对象进行交互,且会提供一个设置器用于改变新的状态对象; 优点: 1. 封装了转换机制; 2. 可以让多个Context 对象共享一个状态对象,从而减少系统中对象个数; 缺点: 1. 状态模式的使用必然增加系统类和对象个数; 2. 状态模式的结构和实现都较为复杂,使用不当导致代码混乱; 3. 状态模式对 “开闭原则” 不太友好,增加新的状态需要修改哪些负责转换状态的源代码; 使用场景: 1. 行为随状态改变而改变的场景 2. 条件、分支语句的取代者 源码应用: ``` - 策略模式 ```angular2html 说明: 一个类的行为或其算法可以在运行时更改。我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象,策略对象改变 context对象的执行算法; 角色: 1. 抽象策略类(Strategy): 定义抽象策略方法 2. 具体策略类(RoadStrategy): 实现各自不同的策略方法 3. 上下文对象context 优点: 1. 算法可以自由切换 2. 避免使用多重条件判断 3. 扩展性好 缺点: 1. 策略类会增多 2. 所有策略类都要对外暴露 使用场景: 1. 如果系统多个类之间的区别仅在于他们的行为,那么使用策略模式可以动态选择一种行为; 2. 一个系统需要动态的在几种算法中选择一种; 源码应用: 1. JDK源码: 数组工具类 Arrays的sort方法 2. SpringMVC源码中: DispatcherServlet中 onRefresh中initLocaleResolver(); ``` - 模板方法模式 ```angular2html 说明: 一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 角色: 1. 抽象基类 2. 具体实现 优点: 1. 封装不变部分,扩展可变部分 2. 提取公共代码,便于维护; 3. 行为由父类控制,子类实现 缺点: 1. 每个不同的实现都需要一个子类来实现 使用场景: 1. 有多个子类共有方法,且逻辑相同 2. 重要的、复杂的方法,可以考虑作为模板方法; 源码应用: 1. Spring源码(RestTemplate等) ``` - 访问者模式 ```angular2html 说明: 使用访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。该模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作; 角色: 1. 抽象元素(Element) 2. 具体元素类(ConcreteElement) 3. 抽象访问者角色(Visitor) 4. 具体访问者角色(ConcreteVisitor) 优点: 1. 符合单一职责 2. 扩展性好,灵活性高 缺点: 1. 违反依赖倒置原则,依赖具体类,没有依赖抽象; 2. 具体元素变更困难 3. 具体元素对访问者公布细节,违反迪米特原则; 使用场景: 源码应用: ```