导图社区 设计模式
详细描述了常见23种设计模式的特点、解决问题,以及优缺点等。干货满满,有需要的朋友赶紧收藏吧!
编辑于2021-11-17 10:07:01设计模式
http://c.biancheng.net/design_pattern/
原则
开闭原则是最基础的设计原则,其它的五个设计原则都是开闭原则的具体形态,也就是说其它的五个设计原则是指导设计的工具和方法,而开闭原则才是其精神领袖。依照java语言的称谓,开闭原则是抽象类,而其它的五个原则是具体的实现类。
1. 开闭原则
抽象化是开闭原则的关键 开闭原则明确的告诉我们:软件实现应该对扩展开放,对修改关闭,其含义是说一个软件实体应该通过扩展来实现变化,而不是通过修改已有的代码来实现变化的。 那什么是软件实体呢?软件实体包括以下几个部分: 项目或软件产品中按照一定的逻辑规则划分的模块 抽象和类 方法 一个软件产品只要在生命周期内,都会发生变化,即然变化是一个事实,我们就应该在设计时尽量适应这些变化,以提高项目的稳定性和灵活性,真正实现“拥抱变化”。开闭原则告诉我们应尽量通过扩展软件实体的行为来实现变化,而不是通过修改现有代码来完成变化,它是为软件实体的未来事件而制定的对现行开发设计进行约束的一个原则。
含义
对扩展开放,对修改关闭
如何使用开闭原则
抽象约束
抽象是对一组事物的通用描述,没有具体的实现,也就表示它可以有非常多的可能性,可以跟随需求的变化而变化。因此,通过接口或抽象类可以约束一组可能变化的行为,并且能够实现对扩展开放,其包含三层含义: 1、通过接口或抽象类约束扩散,对扩展进行边界限定,不允许出现在接口或抽象类中不存在的public方法。 2、参数类型,引用对象尽量使用接口或抽象类,而不是实现类,这主要是实现里氏替换原则的一个要求 3、抽象层尽量保持稳定,一旦确定就不要修改
封装变化
对变化封装包含两层含义: (1) 将相同的变化封装到一个接口或抽象类中 (2) 将不同的变化封装到不同的接口或抽象类中,不应该有两个不同的变化出现在同一个接口或抽象类中。 封装变化,也就是受保护的变化,找出预计有变化或不稳定的点,我们为这些变化点创建稳定的接口。
好处
可以提高代码的可复用性
粒度越小,被复用的可能性就越大;在面向对象的程序设计中,根据原子和抽象编程可以提高代码的可复用性
可以提高软件的可维护性
遵守开闭原则的软件,其稳定性高和延续性强,从而易于扩展和维护。
2. 里式替换原则
定义: 第一种、如果对每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都替换成o2时,程序P的行为没有变化,那么类型T2是类型T1的子类型。 第二种、子类型必须能替换掉它们的基类型 通俗来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能;也就是说:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。 里氏替换原则主要阐述了有关继承的一些原则,也就是什么时候应该使用继承,什么时候不应该使用继承,以及其中蕴含的原理。里氏替换原是继承复用的基础,它反映了基类与子类之间的关系,是对开闭原则的补充,是对实现抽象化的具体步骤的规范。
含义
子类可以扩展父类,但是不能改变父类原有的功能
3. 依赖倒置原则
依赖倒置原则的包含如下的三层含义: 1、高层模块不应该依赖低层模块,两者都应该依赖其抽象 2、抽象不应该依赖细节 3、细节应该依赖抽象 每一个逻辑的实现都是由原子逻辑组成的,不可分割的原子逻辑就是低层模块(一般是接口,抽象类),原子逻辑的组装就是高层模块。在Java语言中,抽象就是指接口和或抽象类,两者都不能被直接实例化。细节就是实现类,实现接口或继承抽象类而产生的类就是细节,可以被直接实例化。下面是依赖倒置原则在Java语言中的表现: 1、模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的 2、接口或抽象类不依赖于实现类 3、实现类依赖于接口或抽象类 更为精简的定义:面向接口编程(Object-Oriented Design, OOD) 每个类尽量都要有接口或抽象类,或者抽象类和接口都有: 依赖倒置原则的基本要求,有抽象才能依赖倒置 变量的表面类型尽量是接口或者抽象类 1、任何类都不应该从具体类派生 2、尽量不要重写基类已经写好的方法(里式替换原则) 3、结合里式替换原则来使用: 结合里式替换原则和依赖倒置原则我们可以得出一个通俗的规则,接口负责定义public属性和方法,并且声明与其他对象的依赖关系,抽象类负责公共构造部分的实现,实现类准确的实现业务逻辑,同时在适当的时候对父类进行细化。 这个是开闭原则的基础,具体内容:面向接口编程,依赖于抽象而不依赖于具体。写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互。
含义
面向接口编程
功能依赖通过接口实现,而不是具体的实现类
4. 单一职责原则
一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因。 一个类(大到模块,小到方法)承担的职责越多,它被复用的可能性就越小,而且一个类承担的职责过多,就相当于将这些职责耦合在一起,当其中一个职责变化时,可能会影响其他职责的运作,因此要将这些职责进行分离,将不同的职责封装在不同的类中,即将不同的变化原因封装在不同的类中,如果多个职责总是同时发生改变则可将它们封装在同一类中。 单一职责原则是实现高内聚、低耦合的指导方针,它是最简单但又最难运用的原则,需要设计人员发现类的不同职责并将其分离,而发现类的多重职责需要设计人员具有较强的分析设计能力和相关实践经验。
5. 接口隔离原则
定义: 1、客户端不应该依赖它不需要的接口 2、类间的依赖关系应该建立在最小的接口上(即:接口中没有多余的方法,所以这里的隔离是指和多余的方法隔离) 注意: 1、接口隔离原则中所说的接口并不是狭意的在Java中用interface定义的接口,而是一种更为宽泛的概念,可以是接口,抽象类或者实体类。 2、将一个臃肿的接口分割为若干个小接口,通过小接口的不同组合可以满足更多的需求。
比较
接口隔离原则和单一职责原则非常类似。单一职责原则要求接口的职责是单一的,而接口隔离原则要求接口尽量细化,它们有异曲同工之妙,都是要让我们的接口功能尽量单一,尽量小。但是,单一职责原则的着重点是在“职责”,而接口隔离原则只单纯地要求接口最小化。那么,如果已经满足单一职责原则的接口,在当前的需求下还可以继续细化,那么还需要细化吗?答案是不要再细化了。在实践中,接口设计的粒度越小,系统就越灵活,这是事实。但是灵活的同时也带来了系统的复杂化,导致开发难度增加。所以接口并不是越小越好,必须要有一个度。当单一职责原则和接口隔离原则存在矛盾时,以满足单一职责原则为底线。
6. 迪米特法则(最少知道原则)
定义:一个对象应该对其他对象保持最少的了解。 即:高内聚,低耦合
7. 合成复用原则
原则是尽量首先使用合成/聚合的方式,而不是使用继承。
概要
凯莉一单接地核
23种设计模式
创建型
该类型的关注点在于通过该类型的设计模式创建出实例
1||| 简单工厂模式(不属于23中设计模式)
简单工厂模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则
特点
一个工厂
细分
普通简单工厂
创建接口Interface Interface的实现类 A,B 工厂类 根据传入的参数确定要调用哪个实现类
工厂方法通过入参决定生产那种产品
多方法简单工厂
创建接口Interface Interface的实现类 A,B 工厂类 给每一个实现类创建一个方法,调用对应的方法即调用对应的实现类
一种产品对应一个方法
静态方法简单工厂
创建接口Interface Interface的实现类 A,B 工厂类 相对于多方法简单工厂,只是把工厂的方法改为了静态方法,不用实例化而已
方法静态访问
使用场景
解决问题
2||| 工厂方法模式
1:功能接口Interf 2:功能实现类A,B 3:工厂接口FactoryInterf (produce方法) 4:该模式就是针对每一个功能实现类创建一个工厂类
特点
一种产品对应一个工厂
使用场景
针对的是一个产品等级结构
3||| 抽象工厂模式
抽象工厂模式和工厂方法模式有点类似,工厂方法模式是针对单个对象创建工厂,而抽象工厂是把多个对象,比如同类型对象放在一个工厂里生产
特点
工厂抽象出来,然后创建不同的工厂,这些工厂可以生产同类型也可以不同类型的产品
类似产品线,每个线都生产全量商品
增加产线很容易,但是增减产品困难
使用场景
针对的多个产品等级结构
解决问题
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类
解决接口选择的问题,根据不同配置或上下文环境加载不同实例
模式描述
抽象工厂
具体工厂
抽象产品
具体产品
效果
实现对象可配置、动态的创建
提高产品移植性
4||| 建造者模式
定义
将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示
使用场景
当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,考虑使用构造者模式
一个复杂的对象,由多个子部件按照一定的步骤组合而成,并且不同部分可以灵活选择,基于不同选择形成不同的产品
一般用在构建流程或者组成部件固定的场合,将这些部件分开构建成为组件对象,再将这些组件对象整合成为目标对象
角色
Product
最终要生成的对象
即产品
Builder
抽象建造者
构建者的抽象基类(有时会使用接口代替)
ConcreteBuilder
具体建造者
Builder的实现类
可多个
Director
指挥者
决定如何构建最终产品的算法
5||| 单例模式
单例对象的类在JVM中只允许一个实例存在 单例模式的关键在于构造方法私有化,然后单例类中实例化,一个对象实例化必须构造方法,由于是在单例类内部调用构造方法,因此可以成功创建实例。
懒汉式
线程不安全 public class Singleton { //构造函数私有化 private Singleton(){} private static Singleton singleton = null; //构建实例化方法 public static Singleton getSingleton(){ if(singleton == null){ singleton = new Singleton(); } return singleton; } }
优点
需要时采取构造,提高启动速度
缺点
获取实例时判断是否实例化,存在线程安全问题,有可能实例化多个
使用场景
单例对象功能复杂,占用内存大,对启动速度有要求
不适用
多线程并发
饿汉式
class Singleton2 { //私有构造函数 private Singleton2(){}; // 直接初始化 private static Singleton2 singleton2= new Singleton2(); public static Singleton2 getSingleton2(){ return singleton2; }
加锁的懒汉式
线程不安全 public class Singleton { //构造函数私有化 private Singleton(){} private static Singleton singleton = null; //构建实例化方法 public static synchronized Singleton getSingleton(){ if(singleton == null){ singleton = new Singleton(); } return singleton; } }
双重判断加锁的懒汉式
// 这种双重加锁的思想很值得学习 class Singleton3{ //私有构造函数 private Singleton3(){} private static Singleton3 singleton3 = null; public static Singleton3 getSingleton3(){ //先检查实例是否存在,如果不存在才进入下面的同步块 if(singleton3 == null){ //同步块,线程安全的创建实例 synchronized (Singleton3.class) { //再次检查实例是否存在,如果不存在才真正的创建实例 if(singleton3 == null){ singleton3 = new Singleton3(); } } } return singleton3; } }
内部静态类实现的懒汉式
这种思想很值得学习 /** * * 静态(类级)内部类:这种方式能达到双检锁方式一样的功效,但实现更简单。 * 对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。 * 这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。 */ class Singleton4{ private Singleton4(){}; /** * * @ClassName: Singleton4Holder * @Description: 类级内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例 * 没有绑定关系,而且只有被调用到时才会装载,从而实现了延迟加载。 * @author: ljx * @date: 2018年12月25日 下午4:04:03 */ private static class Singleton4Holder{ //静态初始化器,由JVM来保证线程安全 private static Singleton4 singleton4 = new Singleton4(); } public static Singleton4 getSingleton4(){ return Singleton4Holder.singleton4; } }
使用枚举
6||| 原型模式
定义
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象
角色
抽象原型类
具体原型类
访问类
使用场景
对象之间相同或相似,即只是个别的几个属性不同的时候
创建对象成本较大,例如初始化时间长,占用CPU太多,或者占用网络资源太多等,需要优化资源
创建一个对象需要繁琐的数据准备或访问权限等,需要提高性能或者提高安全性
系统中大量使用该类对象,且各个调用者都需要给它的属性重新赋值
实例
JSON.parseObject()
BeanUtils.copyProperties
结构型
该类型关注点在于中间转换 为解决怎样组装现有的类,设计他们的交互方式,从而达到实现一定的功能
1||| 适配器模式
定义
将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作
角色
目标(Target)接口
当前系统业务所期待的接口,它可以是抽象类或接口
适配者(Adaptee)类
被访问和适配的现存组件库中的组件接口
适配器(Adapter)类
一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者
使用场景
想要使用一个已经存在的类,但是它却不符合现有的接口规范,导致无法直接去访问,这时创建一个适配器就能间接去访问这个类中的方法
我们有一个类,想将其设计为可重用的类(可被多处访问),我们可以创建适配器来将这个类来适配其他没有提供合适接口的类
需要一个统一的输出接口,而输入端的类型不可预知
优缺点
优点
客户端通过适配器可以透明地调用目标接口
复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类
将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题
在很多业务场景中符合开闭原则
缺点
适配器编写过程需要结合业务场景全面考虑,可能会增加系统的复杂性
增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱
2||| 组合模式
定义
角色
使用场景
优缺点
优点
缺点
3||| 享元(Flyweight)模式
定义
运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率
本质是缓存共享对象,降低内存消耗
4||| 代理模式
解决问题
使用对象只关心自己需要的部分,其他可以通过代理方式实现,而不需要做详细了解
使用场景
按职责来划分,通常有以下使用场景: 1、远程代理。 2、虚拟代理。 3、Copy-on-Write 代理。 4、保护(Protect or Access)代理。 5、Cache代理。 6、防火墙(Firewall)代理。 7、同步化(Synchronization)代理。 8、智能引用(Smart Reference)代理。
拦截器
模式描述
实现与被代理类组合
优缺点
优点
1、职责清晰。 2、高扩展性。 3、智能化。
缺点
1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
5||| 外观模式 (中控模式)
解决问题
降低访问复杂系统的内部子系统时的复杂度,简化客户端与之的接口
模式描述
隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口
为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
优缺点
优点
1、减少系统相互依赖。 2、提高灵活性。 3、提高了安全性。
缺点
不符合开闭原则,如果要改东西很麻烦,继承重写都不合适
使用场景
1、为复杂的模块或子系统提供外界访问的模块。 2、子系统相对独立。 3、预防低水平人员带来的风险。
系统调用了很多子系统,需要了解每一个子系统的接口
实例
Java中的SPI技术
微服务网关
概要
代理重点在于代办,装饰重在资源整合
6||| 桥接(Bridge)模式
https://www.cnblogs.com/meet/p/5116458.html
定义
用于把抽象化与实现化解耦,使得二者可以独立变化
可以将抽象化部分与实现化部分分开,取消二者的继承关系,改用组合关系
个人理解
某个产品实体,存在多种维度的特性
可以将其中一种重点维度作为产品实体类的扩展维度
将每个维度抽象为接口,基于该接口创建多个该维度的实现类,然后再将该接口作为产品实体类的一个维度属性管理即可
角色
抽象化(Abstraction)
定义抽象类,并包含一个对实现化对象的引用
7||| 装饰器模式
定义
指在不改变现有对象结构的情况下,动态地给该对象增加一些职责
解决问题
扩展一个类经常使用继承方法,然后引入了静态特征,并随着扩展功能的增多,子类会膨胀
模式描述
将具体功能职责划分,同时继承装饰者模式
使用场景
1、扩展一个类的功能。
2、动态增加功能,动态撤销。
优缺点
优点
装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
缺点
装饰器模式会增加许多子类,过度使用会增加程序得复杂性
行为型
这些设计模式特别关注对象之间的通信
1||| 责任链模式
解决问题
职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了
模式描述
拦截的类都实现统一接口
优缺点
优点
1、降低耦合度。它将请求的发送者和接收者解耦。 2、简化了对象。使得对象不需要知道链的结构。 3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。 4、增加新的请求处理类很方便。
缺点
1、不能保证请求一定被接收。 2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。 3、可能不容易观察运行时的特征,有碍于除错。
一般是通过next属性进行顺序关联
在Spring中可以借助Ordered接口实现解耦并有序
使用场景
有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定
在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。 3、可动态指定一组对象处理请求
实例
过滤器
拦截器
SpringMVC的参数校验
2||| 命令模式
解决问题
在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适
模式描述
一种数据驱动的设计模式
将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化
定义三个角色:1、received 真正的命令执行对象 2、Command 3、invoker 使用命令对象的入口
使用场景
命令的发起方只需要关心自己要执行的动作(一般不关心结果),然后把该动作发出去,接收方处理详细的细节
3||| 解释器模式
解决问题
对于一些固定文法构建一个解释句子的解释器
模式描述
构建语法树,定义终结符与非终结符
解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。
构建环境类,包含解释器之外的一些全局信息,一般是 HashMap
TerminalExpression 是上下文中的主要解释器
OrExpression、AndExpression 用于创建组合式表达式
使用场景
如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题
4||| 中介者模式
解决问题
对象与对象之间存在大量的关联关系,这样势必会导致系统的结构变得很复杂,同时若一个对象发生改变,我们也需要跟踪与之相关联的对象,同时做出相应的处理。
实现方法
对象 Colleague 之间的通信封装到一个类中单独处理
网状结构分离为星型结构
模式描述
用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护
用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
优缺点
优点
优点: 1、降低了类的复杂度,将一对多转化成了一对一。 2、各个类之间的解耦。 3、符合迪米特原则。
缺点
缺点:中介者会庞大,变得复杂难以维护。
使用场景
1、系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象。 2、想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。
5||| 观察者模式
解决问题
一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
模式描述
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。
实现方法
在抽象类里有一个 ArrayList 存放观察者们
6||| 状态模式
解决问题
对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。
实现方法
通常命令模式的接口中只有一个方法。而状态模式的接口中有一个或者多个方法。而且,状态模式的实现类的方法,一般返回值,或者是改变实例变量的值。也就是说,状态模式一般和对象的状态有关。实现类的方法有不同的功能,覆盖接口中的方法。状态模式和命令模式一样,也可以用于消除 if...else 等条件选择语句
将各种具体的状态类抽象出来
模式描述
使用场景
代码中包含大量与对象状态有关的条件语句
7||| 迭代器模式
解决问题
不同的方式来遍历整个整合对象。
模式描述
这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。
提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。
把在元素之间游走的责任交给迭代器,而不是聚合对象。
8||| 备忘录模式(快照模式)
解决问题
所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。
角色
Originator
发起人,负责创建一个备忘录originator
Memento
备忘录,负责存储Originator对象的内部状态
Caretaker
管理者,负责保存好备忘录Memento
模式描述
通过一个备忘录类专门存储对象状态
客户不与备忘录类耦合,与备忘录管理类耦合。
使用场景
1、需要保存/恢复数据的相关状态场景。 2、提供一个可回滚的操作。
优缺点
优点
1、给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。 2、实现了信息的封装,使得用户不需要关心状态的保存细节
缺点
消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存
9||| 策略模式
解决问题
在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护
实现方法
定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换
实现同一个接口
模式描述
在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改
使用场景
1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
2、一个系统需要动态地在几种算法中选择一种。
3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
10||| 模板方法模式
解决问题
一些方法通用,却在每一个子类都重新写了这一方法
实现方法
将这些通用算法抽象出来。
在抽象类实现,其他步骤在子类实现
模式描述
在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行
创建一个定义操作的 Game 抽象类,其中,模板方法设置为 final,这样它就不会被重写。Cricket 和 Football 是扩展了 Game 的实体类,它们重写了抽象类的方法。
使用场景
1、有多个子类共有的方法,且逻辑相同。 2、重要的、复杂的方法,可以考虑作为模板方法
11||| 访问者模式
解决问题
稳定的数据结构和易变的操作耦合问题
实现方法
在被访问的类里面加一个对外提供接待访问者的接口
在数据基础类里面有一个方法接受访问者,将自身引用传入访问者
模式描述
在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。
主要将数据结构与数据操作分离。
computerParts通过visit接口,允许vistor进行访问
优缺点
优点
1、符合单一职责原则。
2、优秀的扩展性。
3、灵活性
缺点
1、具体元素对访问者公布细节,违反了迪米特原则。 2、具体元素变更比较困难。 3、违反了依赖倒置原则,依赖了具体类,没有依赖抽象。
使用场景
1、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。 2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类
注意
设计模式要活学活用,不要生搬硬套
不能为了使用设计模式而去做架构,而是有了做架构的需求后,发现它符合某一类设计模式的结构,在将两者结合
设计模式只是实现了七大设计原则的具体方式,套用太多设计模式只会陷入模式套路陷阱,最后代码写的凌乱不堪
实际工作中很少会规定必须使用哪种设计模式,这样只会限制别人
设计模式解决的是设计不足的问题,但同时也要避免设计过度
其他类型
MVC
Model(模型) - 模型代表一个存取数据的对象或 JAVA POJO。它也可以带有逻辑,在数据变化时更新控制器。 View(视图) - 视图代表模型包含的数据的可视化。 Controller(控制器) - 控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开。
浮动主题