简介
- 文档
OOA
Object-Oriented Analysis(面向对象分析方法)OOD
Object-Oriented Design(面向对象设计)- UML中的类图及类图之间的关系,参考:uml.md#关系
- 依赖关系(持有对方引用)、关联关系(你中有我,我中有你)、聚合关系、组合关系、泛化关系(继承)和实现关系
- 目标为高内聚,低耦合。耦合度:继承 > 聚合(属性为另外一个对象的引用) > 关联(方法参数或返回值是另外一个对象)
- GRASP模式、JBPM工作流
- 类可以是提取需求中的名词;抽象类和接口的区别:一般是脑子里有一个概念但是没有具体的东西可以设计为抽象类,如交通工具(车、飞机),他有一个方法go();如果只是考虑一类事物和几类事物共同的特征一般设计为接口,如会跑的(Movable),他有一个方法go()
面向对象设计原则
- 开闭原则(Open Closed Principle,OCP)
- 对他人关闭,对自己开放
- 里氏替换原则(Liskov Substitution Principle,LSP)
- 子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法
- 依赖倒置原则(Dependence Inversion Principle,DIP)
- 高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象
- 其核心思想是:要面向接口编程,不要面向实现编程,从而降低类间的耦合性
- 单一职责原则(Single Responsibility Principle,SRP)
- 这里的职责是指类变化的原因,单一职责原则规定一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分
- 接口隔离原则(Interface Segregation Principle,ISP)
- 尽量将臃肿庞大的接口拆分成更小的和更具体的接口,让接口中只包含客户感兴趣的方法。一个类对另一个类的依赖应该建立在最小的接口上
- 与单一职责的区别
- 单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离
- 单一职责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建
- 迪米特法则(Law of Demeter,LoD), 又叫作最少知识原则(Least Knowledge Principle,LKP)
- 只与你的直接朋友交谈,不跟”陌生人”说话。其含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。如:明星与经纪人的关系实例
- 合成复用原则(Composite Reuse Principle,CRP), 又叫组合/聚合复用原则(Composition/Aggregate Reuse Principle,CARP)
- 它要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现
GoF的23种设计模式分类
- 顺口溜
- 抽工 建 单原【抽工建单元(原)】
- 代桥外 享适 组装【在(代)桥外想试组装】
- 迭解 策观 状命中,责备 模访【蝶姐侧观撞(到了)命中(钟),责备(其)模仿】
- 创建型模式(5种)
- 单例(Singleton)模式:某个类只能生成一个实例,且提供一个方法供外部获取该实例
- 原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例
- 工厂方法(FactoryMethod)模式:定义一个用于创建产品的接口,由子类决定生产什么产品
- 抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品
- 建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象
- 结构型模式(7种)
- 代理(Proxy)模式:为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性
- 适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作
- 装饰(Decorator)模式:动态的给对象增加一些职责,即增加其额外的功能
- 外观(Facade)模式:为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问
- 桥接(Bridge)模式:将抽象与实现分离,使它们可以独立变化
- 享元(Flyweight)模式:运用共享技术来有效地支持大量细粒度对象的复用
- 组合(Composite)模式:将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性
- 行为型模式(11种)
- 模板方法(TemplateMethod)模式:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤
- 策略(Strategy)模式:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户
- 命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开
- 职责链(Chain of Responsibility)模式:把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合
- 状态(State)模式:允许一个对象在其内部状态发生改变时改变其行为能力
- 观察者(Observer)模式:多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为
- 中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解
- 迭代器(Iterator)模式:提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部结构
- 访问者(Visitor)模式:在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问
- 备忘录(Memento)模式:在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它
- 解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器
MSB
- 封装(2.1#0:29:23)
- 持有对方引用(2.3#0:17:45)
- 单例模式(5.1#0:10:30-5.3#0:14:30)
- 策略模式(5.3)
- 工厂模式(6.2#0:8:55-6.3#0:53:46)
- 外观模式、中介者模式(7.3#0:55:0)
- 责任链模式(8.2#0:18:40)
- 装饰器模式(9.330:17:30)
- 观察者模式(10.1)
- 组合模式(10.3#0:34:16)
- 享元模式(10.3#0:57:25)
- 代理模式(11)
- 迭代器模式(12.1-12.2#16:00)
- 访问者(12.2#16:00)
尝试
- 访问者模式
- 当解析多个Excel记录(记录了某个人的简历,但是格式可能存在差异)到实体,如果使用访问者模式可以多个访问者同时都有机会读取到某一行记录,但是不好决定用哪一个访问者的姓名解析方法为准来进行姓名解析
创建型模式
- 创建型模式的主要关注点是”怎样创建对象?”,它的主要特点是将对象的创建与使用分离
单例模式(Singleton)
- 单例模式定义
- 某个类只能生成一个实例,且提供一个方法供外部获取该实例
- 例如,Windows 中只能打开一个任务管理器
- 方式
- 饿汉式(
private static final Mgr01_1 INSTANCE = new Mgr01_1();
)- 类加载到内存就实例化一个单例,JVM保证线程安全
- 唯一缺点:不管用到与否,类装载时就完成实例化
- 简单实用,推荐使用
- 懒汉式(synchronized + volatile + 双重检查)
- 用到时再初始化
- 基于内部类(懒汉式)
- 内部类只有在用到时才会加载到内存
- 基于枚举
- 不仅可以线程同步,还可以防止反序列化(其他方法可通过class反序列化获取新的实例)
- 饿汉式(
- 单例模式可扩展为有限的多例模式(Multitcm),这种模式可生成有限个实例并保存在 List 中,客户需要时可随机获取
单例模式示例代码
1 | // 饿汉式 |
原型模式(Prototype)
- 原型模式定义
- 将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例
- 原型实例指定了要创建的对象的种类
- 用这种方式创建对象非常高效,根本无须知道对象创建的细节
- 例如:Windows 操作系统的安装通常较耗时,如果复制就快了很多
- 由于 Java 提供了对象的 clone() 方法,所以用 Java 实现原型模式很简单,需要实现Cloneable接口,并重写clone()方法
- 原型模式的克隆分为浅克隆和深克隆,Java 中的 Object 类提供了浅克隆的 clone() 方法。具体原型类只要实现Cloneable接口就可实现对象的浅克隆,实现深克隆则需要对象属性也实现Cloneable接口
原型模式示例代码
1 | /* |
工厂方法模式(FactoryMethod)
- 工厂方法模式定义
- 定义一个用于创建产品的接口,由子类决定生产什么产品
- 有一个工厂接口定义了生产抽象产品,并且有一个具体的工厂实现了此工厂接口来生产具体的产品
- 简单工厂模式和静态工厂
- 简单工厂模式:如果要创建的产品不多,只要一个工厂类就可以完成(可以创建不同类型的产品)。它不属于 GoF 的 23 种经典设计模式,它的缺点是增加新产品时会违背开闭原则
- 工厂方法模式是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则
- 静态工厂:静态方法产生的类,如单例可认为是一种静态工厂
- 简单工厂模式:如果要创建的产品不多,只要一个工厂类就可以完成(可以创建不同类型的产品)。它不属于 GoF 的 23 种经典设计模式,它的缺点是增加新产品时会违背开闭原则
- 工厂方法模式由工厂接口类、具体工厂、产品接口类和具体产品等4个要素构成
- 类图
抽象工厂模式(AbstractFactory)
- 抽象工厂模式定义
- 提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品
- 抽象工厂模式是工厂方法模式的升级,工厂方法模式只生产一个类型的产品,而抽象工厂模式可生产一系列产品
- 如 java 的 AWT 中的 Button 和 Text 等构件在 Windows 和 UNIX 中的本地实现是不同的
- 系统一次只可能消费其中某一系列产品,即同族的产品一起使用
- 其缺点是:当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改
- 类图
建造者模式(Bulider)
- 建造者模式定义
- 将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象
- 它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的
- 建造者模式和工厂模式的关注点不同:建造者模式注重零部件的组装过程,而工厂方法模式更注重零部件的创建过程,但两者可以结合使用
建造者模式示例代码
1 | // 简单的Person Model省略 |
结构型模式
- 结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象
代理模式(Proxy)
- 代理模式定义
- 为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性
- 个人理解,对象适配器和装饰器模式类似代理模式:实现目标接口;代理对象/适配器/装饰器构造时传入目标对象(可嵌套),当然也可不传入目标对象,而是传入一些配置,从而动态获取目标对象
- 适配器模式主要解决接口转换的问题,代理模式主要解决对象无法直接访问的问题,装饰者模式主要用来增强功能
- 代理分为静态代理和动态代理,其中动态代理主要有JDK动态代理和Cglib动态代理,最终都是基于ASM操纵字节码
- JDK动态代理和静态代理类似,代理类和被代理需要实现相同的接口
- Cglib动态代理是生成被代理类的子类,因此被代理类不能被final修饰
- Spring AOP基于动态代理完成,参考spring.md#AOP
- (静态代理)类图
静态代理示例代码
1 | /*结果如: |
动态代理示例代码
1 | // 1.JDK动态代理。还是Dog 和 Movable,同上文 |
适配器模式(Adapter)
- 适配器模式定义
- 将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作
- 如
InputStreamReader
就是Adapter模式。FileInputStream默认只能一个字节一个字节的读,此时通过InputStreamReader适配,最后可使用BufferedReader进行一行一行的读,如new BufferedReader(new InputStreamReader(new FileInputStream("c:/test.text")));
- 常见的以Adapter命名的反而不是基于适配器模式,如WindowAdapter(主要为了方便编程,对接口的方法有默认的实现,只需要去继承重写关心的方法);常见以Bridge命名却有可能是Adapter模式
- 个人理解,对象适配器和装饰器模式类似代理模式:实现目标接口;代理对象/适配器/装饰器构造时传入目标对象(可嵌套)
- 适配器模式分为类适配器和对象适配器两种
- 类适配器基于继承
- 对象适配器基于依赖
- 对象适配器相对类适配器耦合度更低,更常用
- 类图
适配器模示例代码
1 | public static void main(String[] args) { |
装饰模式(Decorator)
- 装饰模式定义
- 动态的给对象增加一些职责,即增加其额外的功能
- 装饰器可混合使用,装饰器接口和被装饰物实现了同一个接口。如InputStream相关的类
- 个人理解,对象适配器和装饰器模式类似代理模式:实现目标接口;代理对象/适配器/装饰器构造时传入目标对象(可嵌套)
- 如:房子框架搭建完成 - 房子墙面刷白完成 - 房子周围花园建造完成
外观模式(Facade)
- 外观模式(又称门面模式)定义(Facade读音:/fəˈsɑːd/)
- 为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问
- 将复杂的关系封装到一起,再对外提供服务。如政务服务的统一窗口,用户只需给该窗口提交资料,至于政务内部涉及到多个部门审核由统一窗口协调,对用户不可见
- 此时对外认为是外观(或门面),对内认为是调停者模式/中介者模式(有了调停者,当内部有新加入成员时,只需要给调停者打交道,不需要和其他成员打交道)
- 类图
外观模式示例代码
1 | public static void main(String[] args) { |
桥接模式(Bridge)
- 桥接模式定义
- 将抽象与实现分离,使它们可以独立变化
- 如Controller持有Service引用
享元模式(Flyweight)
https://www.runoob.com/design-pattern/flyweight-pattern.html
- 享元模式定义
- 运用共享技术来有效地支持大量细粒度对象的复用
- 如java中的常量字符串,有一个常量池,如果下次需要的常量字符串在这个里面有则直接使用
享元模式示例代码
1 | public static void main(String[] args) { |
组合模式(Composite)
- 组合模式定义
- 将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性
- 透明方式和安全模式
- 透明方式
- 在该方式中,由于抽象构件声明了所有子类中的全部方法,所以客户端无须区别树叶对象和树枝对象,对客户端来说是透明的
- 其缺点是:树叶构件本来没有 add()、remove() 方法,却要实现它们(空实现或抛异常),这样会带来一些安全性问题
- 安全方式
- 将管理子构件的方法移到树枝构件中,抽象构件和树叶构件没有对子对象的管理方法,这样就避免了透明方式的安全性问题
- 但由于叶子和分支有不同的接口,客户端在调用时要知道树叶对象和树枝对象的存在,所以失去了透明性
- 透明方式
组合模式(透明方式)示例代码
1 | /* |
行为型模式
- 行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配
- 行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为
模板方法模式(TemplateMethod)
- 模板方法模式定义
- 定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤
- 如自定义ClassLoader时,需要继承ClassLoader并重写其findClass这个抽象方法
模板方法模式示例代码
1 | public static void main(String[] args) { |
策略模式(Strategy)
- 策略模式定义
- 定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户
java.util.Comparator
比较策略,需要实现compare方法,使用了策略模式。如:Collections.sort(list, Comparator); 需传入被排序集合和排序策略java.lang.Comparable
可排序的,需实现compareTo方法
策略模式示例代码(基于Comparator使用)
1 | public static void main(String[] args) { |
命令模式(Command)
- 命令模式定义
- 将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开
命令模式示例代码
1 | public static void main(String[] args) { |
职责链模式(Chain of Responsibility)
- 职责链模式定义
- 把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合
- servelet中的Filter和FilterChain就是使用了责任链模式
职责链模式示例代码
1 | public static void main(String[] args) { |
状态模式(State)
- 状态模式定义
- 允许一个对象在其内部状态发生改变时改变其行为能力
- 此时将状态抽象出来,并让state对象去执行行为,此时传入不同的state对象。如果state的类型不会增加,其实switch case即可
状态模式示例代码
1 | /* |
观察者模式(Observer)
- 观察者模式定义
- 多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为
- Observer、Listener、Hook、Callback都属于观察者模式
观察者模式示例代码
1 | /* |
中介者模式(Mediator)
- 中介者模式(又称调停者模式)定义
- 定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解
中介者模式(简单实现)示例代码
1 | /* |
迭代器模式(Iterator)
- 迭代器模式定义
- 提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部结构
- 参考集合的Iterator实现
访问者模式(Visitor)
- 访问者模式定义
- 在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问
- 访问者模式能把处理方法从数据结构中分离出来,并可以根据需要增加新的处理方法,且不用修改原来的程序代码与数据结构,这提高了程序的扩展性和灵活性
- 如:顾客在商场购物时放在购物车中的商品,顾客主要关心所选商品的性价比,而收银员关心的是商品的价格和数量,并且不同的顾客评价不一
某对象.accept(访问者)
=> 某对象接受访问者的访问 => 某对象会调用访问者的visit方法
- 优点
- 扩展性好。能够在不修改对象结构的情况下,为对象结构中的元素添加新的功能
- 符合单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一
- 缺点
- 增加新的元素类很困难。在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了开闭原则
- 破坏封装。具体方法被从类中抽离出来了
- 违反了依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类
访问者模式示例代码
1 | /* |
备忘录模式(Memento)
- 备忘录模式定义
- 在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它
- 类似的可以将所有的类和属性实现Serializable接口,则可进行序列化存盘
- 主要用在存盘,如游戏存档
备忘录模式示例代码
1 | /* |
解释器模式(Interpreter)
- 解释器模式定义
- 提供如何定义语言的文法,以及对语言句子的解释方法,即解释器
解释器模式示例代码
1 | /* |
结合Spring案例
工厂模式、模板模式和策略模式的混合使用
- 在实际开发的过程当中,最常用的还是设计模式还是 工厂+模板+策略模式,通过模板抽象出业务流程的通用逻辑固化下来,再使用简单工厂模式生成对应的策略逻辑
- 参考
- 实例化方式
- 结合Spring可使用继承
InitializingBean
将Bean实例化后自动注册到工厂中,但是这样存在问题服务中不能使用自定义属性 - 通过工厂if-else进行实例化,每个请求都调用工厂实例化方法,在子类中使用Spring Bean时使用SpringU进行获取
- 优化方案: 在工厂初始化时,基于org.reflections#reflections包自动获取接口的实现类,进行一次类信息注册(不足: 需要反射一次调用注册方法);之后调用每次获取此类进行反射实例化
- 结合Spring可使用继承
参考文章