设计原则
1、开闭原则(Open Close Principle,OCP)
一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展 开闭原则即对拓展开放,对修改关闭,要想在功能新增的时候不修改原先的代码,即程序中使用的是抽象类或者接口,我们新增的子类或者实现,这样才能保障我们拓展功能的时候不用修改原先的代码,所以说抽象化是开闭原则的关键。
2、里氏替换原则(Liskov Substitution Principle,LSP)
所有引用基类(父类)的地方必须能透明地使用其子类的对象 基类存在的地方必定能被子类替换,且功能不会发生影响。里氏替换原则是“开-闭原则的补充”。
里氏替换通俗来讲就是子类可以拓展父类但是不能改变父类已确定的行为,包含以下四个含义:
· 子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法
· 子类中可以增加自己特有的方法
· 当子类重载父类方法时,形参的范围要比父类的形参范围大,这样别的类调用这个方法时会优先匹配父类的实现而不是子类的实现
· 当子类实现父类的抽象方法时,方法的返回值要比父类更严格,也可以说成更具体
3、依赖倒转原则(Dependence Inversion Principle,DIP)
抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程 依赖倒转原则要求我们在程序代码中传递参数时或在关联关系中,尽量引用层次高的抽象层类,即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型的转换等,而不要用具体类来做这些事情。
在实现依赖倒转原则时,我们需要针对抽象层编程,而将具体类的对象通过依赖注入(Dependency Injection, DI)的方式注入到其他对象中,依赖注入是指当一个对象要与其他对象发生依赖关系时,通过抽象来注入所依赖的对象。常用的注入方式有三种,分别是:构造注入,设值注入(Setter注入)和接口注入。构造注入是指通过构造函数来传入具体类的对象,设值注入是指通过Setter方法来传入具体类的对象,而接口注入是指通过在接口中声明的业务方法来传入具体类的对象。这些方法在定义时使用的是抽象类型,在运行时再传入具体类型的对象,由子类对象来覆盖父类对象。
开闭原则是目标,里氏代换原则是基础,依赖倒转原则是手段。
4、单一职能原则(Single Responsibility Principle, SRP)
一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因 类的功能尽量单一,这样才能提高复用率。
单一职能原则是软件高内聚低耦合的指导方针。
5、接口隔离原则(Interface Segregation Principle, ISP)
使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口 每一接口都应该承担一种相对独立的角色,该干的事情都敢,不该干的事情都不敢。
6、迪米特法则(Law of Demeter, LoD)
一个软件实体应当尽可能少地与其他实体发生相互作用。** 类与类之间的耦合度应尽量的低,这样如果类发生变化,影响才会最小。
不要和陌生人说话,只和你的直接朋友通信,直接朋友包含如下:
· 当前对象本身(this)
· 作为参数的对象
· 成员变量
· 集合成员变量中的元素
· 创建的对象
可以通过使用第三者类来降低两个类的耦合度。
7、合成复用原则(Composite Reuse Principle, CRP)
尽量使用对象组合,而不是继承来达到复用的目的 通过继承来实现复用的问题是它会破坏封装性,子类能够看到基类的实现细节,自身类通过继承而来的基类方法是静态的(因为继承的类是确定的),而通过调用成员变量去执行成员变量的方法是动态的,因为成员变量可以是抽象化的,而实现类可以有很多种,每一种实现类的功能又不相同,但是他们可以被当作同一类型而被引入。
设计模式
创建型
1、工厂方法模式(FactoryMethodPattern)(延迟到子类来选择实现)
-
定义
定义一个用于创建对象的接口,让子类来决定实例化哪一个类
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lT7H4oae-1692548461350)(…\Pictures\blog\designpattern_uml\factoryMethod.png)]
-
优缺点
类的个数容易过多,增加复杂性;增加了系统的抽象性和理解难度
-
适用场景
- 创建对象需要大量重复的代码
- 应用层不依赖于产品实例如何被创建、实现等细节
- 一个类通过其子类来指定创建哪个对象
-
源码体现
2、抽象工厂模式(AbstractFactoryPattern)(选择产品簇的实现)
-
定义
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FAHPCtzR-1692548461351)(…\Pictures\blog\designpattern_uml\abstractFactory.png)]
-
优缺点
规定了所有可能被创建的产品集合,产品簇中扩展新的产品困难,需要修改抽象工厂的接口;增加了系统的抽象性和理解难度
-
适用场景
-
源码体现
3、单例模式(SingletonPattern)(确保对象的唯一性)
-
定义
保证一个类只有一个实例,并提供一个访问它的全局访问点
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UfhBEBrt-1692548461351)(…\Pictures\blog\designpattern_uml\singleton.png)]
-
优缺点
保证一个类只有一个实例,减少了内存开销,
-
适用场景
-
源码体现
J2EE标准中的ServletContext、ServletContextConfig等,Spring中的ApplicationContext、数据库连接池等
4、原型模式(PrototypePattern)(对象的克隆)
-
定义
用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jmx0ATC6-1692548461352)(…\Pictures\blog\designpattern_uml\prototype.png)]
-
优缺点
- 性能优良,Java自带的原型模式是基于内存二进制流的拷贝,比直接new一个对象性能上提升很多
- 可以使用深克隆方式保存对象的状态,可以在需要的时候实现撤销操作
- 实现深克隆时,需要编写较为复杂的代码,当对象之间存在多重嵌套引用时,每一层对象对应的类都必须支持深克隆,实现起来比较麻烦,可以考虑使用序列化实现深克隆
- 克隆方法位于类的内部,当类改变时,需要修改代码
-
适用场景
- 类的初始化消耗资源较多
- new产生一个对象需要非常繁琐的过程(数据准备、访问权限等)
- 构造函数比较复杂
- 循环体中生产大量对象时
-
源码体现
- Spring中scope="prototype"的类
- JSON.parseObject()
5、建造者模式(BuilderPattern)(复杂对象的构建)(分离整体构建算法和部件构造)
-
定义
将一个复杂对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f2sqPZMj-1692548461352)(…\Pictures\blog\designpattern_uml\builder.png)]
Builder接口定义如何构造各个部件,也就是知道每个部件功能如何实现,以及如何装配这些部件到产品中。Director知道如何组合来构建产品,负责整体的构建算法,通常是分步骤来执行。客户端可以创建Direcctor,Director去调用Builder。也可以直接在客户端调用Builder,相当于将客户端和Director融合在一起,这是一种简化的方式,用的比较多
-
优缺点
- 松散耦合,创建和使用分离
- 可以很容易改变产品的内部表示
- 复用性好
- 产品内部发生变化,建造者都要修改,成本较大
-
适用场景
- 创建对象需要很多步骤,但是步骤的顺序不一定固定,如果一个对象有非常复杂的内部结构(很多属性),可以将复杂对象的创建和使用进行分离
- 相同的方法,不同的执行顺序,产生不同的结果时
- 多个部件都可以装配到一个对象中,但是产生的结果又不相同
- 当初始化一个对象特别复杂,参数多,而且很多参数都具有默认值
-
源码体现
- JDK的StringBuilder的append()方法开放构造步骤,最后调用toString()方法就可以获得一个构造好的完整字符串
- MyBatis中的CacheBuilder、SqlSessionFactoryBuilder最后调用相应的build()方法来获取对应的类
- Spring中的BeanDefinitionBuilder调用getBeanDefinition()获得BeanDefinition对象
结构型
6、装饰器模式(DecoratorPattern)(功能扩展)(动态组合)
-
定义
动态的给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更灵活,别名包装模式
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eDqKF2d1-1692548461352)(…\Pictures\blog\designpattern_uml\decorator.png)]
透明且动态地扩展类的功能
-
优缺点
- 装饰器是继承的有力补充,比继承更为灵活
- 通过使用不同的装饰类以及它们的排列组合,可以实现不同的效果
- 完全遵守开闭原则,复用性好
- 会出现更多的类,增加程序复杂性,动态装饰时,多层装饰会更复杂
-
适用场景
- 用于扩展一个类的功能或给一个类添加附加职责
- 动态的给一个对象添加功能,这些功能可以再动态的撤销
- 需要为一批兄弟类进行改装或加装功能
-
源码体现
- JDK中跟IO相关的类
- Spring中处理事务缓存的TransactionAwareCacheDecorator,MVC中的HttpHeadResponseDecorator类
- MyBatis中的缓存类Cache
7、适配器模式(AdapterPattern)(不兼容结构的协调)
-
定义
将一个接口转换客户希望的另一个接口,使接口不兼容的那些类能够一起工作,又名包装器(Wrapper)。适配器模式可以为对象适配器模式,也可以是类适配器模式
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nYxBytR9-1692548461352)(…\Pictures\blog\designpattern_uml\adapter.png)]
-
优缺点
- 能提高类的透明性和复用性,现有的类复用但不需要改变
- 目标类和适配器类解耦,提高程序的扩展性
- 在很多业务中符合开闭原则
- 适配器编写过程需要全面考虑,可能会增加系统的复杂性
- 增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱
-
适用场景
- 已经存在的类,它的方法和需求不匹配
- 适配器模式不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品、不同厂家造成功能类似而接口不相同情况下的解决方案
-
源码体现
- SpringAOP中的AdvisorAdapter类,它有三个实现类MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter和ThrowsAdviceAdapter。SpringMVC中的HandlerAdapter也有好几个子类
8、组合模式(CompositePattern)(树形结构的处理)(统一叶子对象和组合对象)
-
定义
将对象组合成树形结构以表示“部分-整体”的层次结构。使得用户对单个对象和组合对象的使用具有一致性
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7U2V4aWX-1692548461353)(…\Pictures\blog\designpattern_uml\composite.png)]
透明组合模式:所有公共方法都定义在Component中,这样的好处是客户端无需分辨是Leaf和Composite,它们具有完全一致的接口,就像上面展示的一样
安全组合模式:只规定系统各个层次的最基础的一致行为,而把Composite本身的方法(管理子对象的添加删除等)放到自身当中
-
优缺点
- 定义了包含基本对象和组合对象的类层次结构
- 统一了组合对象和叶子对象
- 简化了客户端的调用
- 更容易扩展
- 很难限制组合中的组件类型
- 使设计变得更加抽象
-
适用场景
- 希望客户端可以忽略组合对象与单个对象的差异时
- 对象层次具备整体和部分,呈树形结构,比如树形菜单,目录结构,公司组织架构等
-
源码体现
- MyBatis中的SqlNode
9、门面模式(FacadePattern)(子系统的统一入口)(封装交互,简化调用)
-
定义
又称为门面模式,为子系统中一组接口提供一个统计的入口。外观模式定义了一个高层接口,方便了客户端与子系统之间的访问。
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kYFMM4KK-1692548461353)(…\Pictures\blog\designpattern_uml\facade.png)]
-
优缺点
- 简化了调用过程,无需深入了解子系统,以防止给子系统带来风险
- 减少系统依赖、松散耦合
- 更好地划分层次访问,提高了安全性
- 遵循迪米特法则(最少知道原则)
- 当增加子系统和扩展子系统行为时,可能容易带来未知风险
- 不符合开闭原则
- 某些情况下违背单一职责原则
-
适用场景
- 子系统越来越复杂,增加门面提供简单接口
- 构建多层系统结构,利用门面对象作为每层的入口,简化层间调用
-
源码体现
- Spring JDBC模块下的JdbcUtils工具类,封装了和JDBC相关的所有操作
- MyBatis中的Configuration中的很多以new开头的方法
- Tomcat中的RequestFacade封装了很多request的操作
10、桥接模式(BridgePattern)(处理多个维度的变化)(分离抽象和实现)
-
定义
将抽象部分和它的实现部分分离,使它们都可以独立的变化,别名柄体模式,接口模式
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-61SitgqU-1692548461353)(…\Pictures\blog\designpattern_uml\bridge.png)]
桥接模式就是面向抽象编程的体现,可以说Java中无处不桥接
-
优缺点
- 分离抽象和实现
- 提高扩展性
- 符合开闭原则、合成复用原则
- 需要正确地识别系统中两个独立变化的维度,这是有难度的
-
适用场景
- 在抽象和具体实现之间需要增加更多灵活性的场景
- 一个类存在两个或多个独立变化的维度,而这两个或多个维度都需要独立进行扩展
- 不希望使用继承,或因为多层继承导致系统类个数剧增
-
源码体现
- JDBC API中的DriverManager就是桥
- 表现层调用逻辑层,逻辑层调用数据层
11、代理模式(ProxyPattern)(控制对象的访问)
-
定义
为其它对象提供一种代理以控制对这个对象的访问
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6yUk1cHl-1692548461354)(…\Pictures\blog\designpattern_uml\proxy.png)]
-
优缺点
- 将代理对象与真实被调用的目标对象分离
- 在一定程度上降低了系统耦合性,扩展性好
- 可以起到保护目标对象的作用
- 可以增强目标对象的功能
- 客户端与目标对象中增加一个代理对象,会导致请求处理速度变慢
- 增加了系统的复杂度
-
适用场景
- 需要创建开销很大的对象的时候,可以使用虚代理
- 需要控制对目标对象的访问的时候,可以使用保护代理
- 需要在访问对象的时候执行一些附加操作的时候,可以使用智能代理
- 需要为一个对象在不同的地址空间提供局部代表的时候,可以使用远程代理
-
源码体现
- Spring中ProxyFactoryBean的getObject()方法;利用动态代理实现AOP时的两个很重要的类JdkDynamicAopProxy和CgLibAopProxy
- Mybatis整合Spirng中mapper接口的实现类就是通过代理实现的
12、享元模式(FlyweightPattern)(实现对象的复用)(分离与共享)
-
定义
将系统中细粒度的相同的或者相似的对象以共享的方式存放在享元池中供客户端使用。
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CTiRxaEJ-1692548461354)(…\Pictures\blog\designpattern_uml\flyweight.png)]
享元模式把一个对象的状态分为内部状态和外部状态,内部状态是不变的,外部状态是变化的;然后通过共享不变的部分,达到减少对象数量并节约内存的目的。比如数据库的连接对象的用户名、密码、url、驱动等信息,在创建对象时就已经设置好了,不会随环境改变而改变,这些就是内部状态。当连接要回收利用时,需要给它标记为可用状态,这个就是外部状态
-
优缺点
- 减少对象数量,节省内存空间
- 维护共享对象,需要额外开销
-
适用场景
- 当系统中多处需要同一组信息时,可以把这些信息封装到一个对象中,然后对该对象进行缓存。常常用于系统底层的开发,已解决系统的性能问题
- 系统有大量相似对象、需要缓冲池的场景
-
源码体现
- JDK中的String、Integer、Long等类
- Apache Commons Pool中的对象池
行为型
13、策略模式(StrategyPattern)(算法的封装与切换)(分离算法与选择实现)
-
定义
定义一系列的算法,把它们一个一个封装起来,并且使它们可以相互替换,别名政策模式
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-40ZzQJJE-1692548461354)(…\Pictures\blog\designpattern_uml\strategy.png)]
-
优缺点
- 符合开闭原则、里氏替换原则
- 避免使用多重条件转移语句,如if…else…、switch语句
- 可以提高算法的保密性和安全性
- 代码中产生非常多的策略类,增加维护难度
- 客户端必须知道所有的策略,并且自行决定使用哪一个
-
适用场景
- 针对同一类型问题,有多种处理方式,每一种都能独立解决问题
- 算法需要自由切换的场景
- 需要屏蔽算法规则的场景
-
源码体现
- JDK中的Comparator接口
- Spring中的Resource类,Spring初始化也采用了策略模式,接口InstantiationStrategy
14、模板方法模式(TemplateMethodPattern)(固定算法骨架)
-
定义
定义一个操作中的算法骨架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OkKrHqNC-1692548461355)(…\Pictures\blog\designpattern_uml\templateMethod.png)]
什么时候需要使用抽象类而不是接口:既要约束子类的行为,又要为子类提供公共的功能
如果某些方法,不希望子类访问,访问修饰符为private,如果需要子类访问,将方法设置为protected final
模板方法中如果需要某些对象实例,可以考虑通过工厂方法模式来获取,将具体的构建对象的实现延迟到子类
-
优缺点
- 相同的逻辑放到抽象父类中,实现代码复用
- 不同的代码放到子类中,通过对子类扩展增加新的行为,提高了代码的扩展性
- 算法骨架不容易升级
- 继承自身的缺点,父类增加新抽象方法,子类都要改
-
适用场景
- 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现的情况
- 各子类中公共的行为被提取出来并集中到一个公共的父类中,从而避免代码重复
- 需要控制子类扩展的情况,模板方法会在特定的点调用子类的方法,这样就能进行控制
-
源码体现
- JDK中的AbstractList中的get(int index)
- HttpServlet中的service()、doGet()、doPost()方法
- JUC中的AQS
- Mybatis中基础的SQL执行类BaseExecutor,实现了大部分的逻辑,其余几个方法交给子类定制化完成
15、观察者模式(ObserverPattern)(对象间的联动)
-
定义
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,依赖于它的对象都得到通知并被自动更新,又被称为发布-订阅模式、模型-视图模式、源-监听者模式。
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0Ps3BmUv-1692548461355)(…\Pictures\blog\designpattern_uml\observer.png)]
观察者接收事件的两种模型
推模型是假定目标对象知道观察者需要的数据,这种模式可能会使得观察者对象难以复用,因为观察者定义的update方法是按需而定义的,可能无法兼顾没有考虑到的情况,这就意味着出现新情况的时候,就可能需要提供新的update方法,或者干脆重新实现观察者
拉模型是目标对象不知道观察者具体需要什么数据,只能将自身传给观察者,让观察者自己去按需取值,这种方式基本上可以适应各种情况的需要
-
优缺点
- 观察者和被观察者之间是抽象耦合的
- 观察者模式实现了动态联动
- 支持广播通信、支持事件注册、支持兴趣分发
- 事件通知是线性关系,如果其中一个观察者卡壳,会影响后续的观察者接收该事件
- 可能会引起无谓的操作,比如观察者和被观察者存在循环依赖,会造成循环调用,导致系统崩溃
-
适用场景
- 当一个抽象模型包含两个方面的内容,其中一个方面依赖于另一个方面
- 其它一个或多个对象的变化依赖于另一个对象的变化
- 实现类似广播机制的功能
- 多层级嵌套使用,形成一种链式触发机制,使得事件具备跨域(跨越两种观察者类型)通知
-
源码体现
- Sping中ContextLoaderListener实现了ServletContextListener接口,而该接口又继承了EventListener,在JDK中EventListener有非常广泛的应用
- JDK自带的两个接口:Observable和Observer
16、访问者模式(VisitorPattern)(操作复杂对象结构)(预留通路,回调实现)
-
定义
表示一个作用于某对象结构中的各元素的操作,它使得你可以不改变各元素的类的前提下,定义作用于这些元素的新操作
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X226xCVQ-1692548461355)(…\Pictures\blog\designpattern_uml\visitor.png)]
元素的类层次和访问者的类层次
两次动态分发:第一次是对元素的分发visit方法,第二次是对访问者的分发accept方法
访问者模式的核心是,解耦数据结构与数据操作,使得对元素的操作具备优秀的扩展性
-
优缺点
-
解耦了数据结构与数据操作,使得操作可以独立变化
-
扩展性好:可以通过扩展访问者角色,实现对数据集的不同操作
-
元素具体类型并非单一,访问者均可操作
-
各角色职责分离符合,单一职责原则
-
无法增加元素类型:若对象结构易于变化,则访问者必须增加对应元素类型的操作,违背了开闭原则
-
具体元素变更困难:具体元素增加、删除属性等操作会导致对应的访问者类需要进行相应的修改
-
破坏封装:访问者可能会调用对象中的属性或方法
-
违背依赖倒置原则:访问者依赖的是具体元素类型,而不是抽象
-
-
适用场景
- 数据结构稳定,作用于数据结构的操作经常变化的场景,(一个操作对应一个访问者)
- 需要数据结构与数据操作分离的场景
- 需要对不同数据类型(元素)进行操作,而不使用分支判断具体类型的场景
-
源码体现
- JDK的NIO模块下的FileVisitor,提供了递归遍历文件树的支持,允许在文件被访问、目录将被访问、目录已被访问、发生错误等过程上进行控制(都有相应的钩子程序)
- Spring IoC中的BeanDefinitionVisitor的visitBeanDefinition方法中分别访问了其它的数据,比如父类的名称、自己的类名、在IoC容器中的名称等信息。
17、命令模式(CommandPattern)(请求发送者和请求接受者解耦)(封装请求)
-
定义
将一个请求封装成一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录日志,以及支持可撤销的操作,别名为Action模式或Transaction模式
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cifSatpW-1692548461355)(…\Pictures\blog\designpattern_uml\command.png)]
命令的参数化配置指的是可以在Invocker中定义多个命令对象的引用,来表示不同的命令
-
优缺点
- 通过引入中间件(抽象接口),解耦了命令请求与实现
- 支持组合命令,支持命令队列
- 更好的扩展性,可以很容易的增加新命令
- 具体的命令类可能会过多,增加了理解上的困难
-
适用场景
- 现实语义中具备"命令"的操作(如命令菜单,shell命令…)
- 请求调用者和请求接受者需要解耦
- 需要支持撤销和恢复的操作
- 需要支持宏命令(命令的组合)
- 需要支持当系统崩溃时的恢复功能,可以采用日志命令的方式
-
源码体现
- JDK的Runnable接口,实际上就相当于是命令的抽象
- Junit的Test接口
18、备忘录模式(MementoPattern)(撤销功能的实现)(保存和恢复内部状态)
-
定义
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象外保存这个状态,以便将来回滚到这一状态,别名快照模式,令牌模式
-
结构[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wp6Cl2E9-1692548461356)(…\Pictures\blog\designpattern_uml\memento.png)]
-
优缺点
- 简化原发器职责,隔离状态存储与获取,实现了信息的封装
- 提供状态回滚功能
- 消耗资源:如果保存的状态过多时,会很消耗内存
-
适用场景
- 需要保存一个对象在某一时刻的全部或部分状态,以便在后续恢复的场景
- 希望在对象之外保存状态,且除了自己其它类对象无法访问保存的具体内容
-
源码体现
19、状态模式(StatePattern)(处理对象的多种状态及其相互转换)(根据状态来分离和选择行为)
-
定义
复杂对象不同状态下的行为封装与状态转换。
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wY2rX8JY-1692548461356)(…\Pictures\blog\designpattern_uml\state.png)]
-
优缺点
- 结构清晰:将状态独立为类,消除了冗余的if…else…或switch语句,使代码更简洁,提高系统可维护性
- 将状态转换显示化:通常对象内部都是使用数值类型来定义状态,状态的切换是通过赋值进行表现,不够直观;而使用状态类,在切换状态时,是以不同的类进行表示,转换目的更加明确
- 状态类职责明确且具备扩展性
- 类膨胀:如果一个事物具备很多状态,则造成状态类太多
- 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱
- 状态模式对开闭原则的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,修改某个状态类的行为也需要修改对应类的源代码
-
适用场景
- 行为随状态改变而改变的场景
- 一个操作中含有庞大的多分支结构,并且这些分支取决于对象的状态
-
源码体现
20、迭代器模式(IteratorPattern)(遍历聚合对象中的元素)
-
定义
提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示,别名为游标
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4090n9n3-1692548461356)(…\Pictures\blog\designpattern_uml\iterator.png)]
-
优缺点
- 多态迭代:一个迭代接口可以访问不同的集合对象
- 简化集合接口:将集合本身应该提供的元素迭代接口抽取到迭代器中,使集合无需关系具体迭代行为
- 元素迭代功能多样化:每个集合都可以提供一个或多个不同的迭代器,使得同种聚合结构可以有不同的迭代行为
- 对于比较简单的遍历使用迭代器方式比较繁琐
-
适用场景
- 访问一个集合对象的内容而无需暴露它的内部表示
- 屏蔽了内部元素获取细节,为外部提供了一个统一的访问接口,解耦了元素迭代与集合对象间的耦合
- 通过提供不同的迭代器,可以为同个集合对象提供不同顺序的元素访问行为,扩展了集合对元素的迭代功能
-
源码体现
- JDK中的Iterator接口的实现类
- Mybatis中的DefaultCursor实现了Cursor接口,定义了一个成员变量cursorIterator,该成员变量是其内部类并且实现了JDK中的Iterator接口
21、责任链模式(ChainOfResponsibilityPattern)(请求的链式处理)(分离职责,动态组合)
-
定义
将请求发送者和请求接受者解耦,让请求的接受者形成链式操作,并沿着该链传递请求,直到有人处理请求。
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m13Tk7TY-1692548461357)(…\Pictures\blog\designpattern_uml\responsibility.png)]
-
优缺点
- 将请求与处理解耦
- 请求处理者只需关注自己感兴趣的请求处理即可,对不感兴趣的请求,直接转发给下一级节点
- 具备链式传递处理请求功能,发送至无需知晓链路结构,只需等待处理结果
- 链路结构灵活,可以通过改变链路结构动态地新增或删减责任
- 易于扩展新的请求处理类,符合开闭原则
- 责任链过长或处理时间过长,会影响整体性能
- 如果节点存在循环引用,会导致死循环,导致系统崩溃
-
适用场景
- 多个对象可以处理同一个请求,但具体由哪个对象处理则在运行时动态决定
- 可以动态指定处理一个请求的职责对象集合
-
源码体现
- J2EE中的Filter接口
- Netty中的ChannelPipeline接口
22、中介者模式(MediatorPattern)(协调多个对象间的交互)(封装交互)
-
定义
用一个中介对象来封装一系列的对象交互。中介者使得各对象不需要显式地相互引用,从而使其松散耦合,而且可以独立的改变它们之间的交互
-
结构[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rh6dlqZR-1692548461357)(…\Pictures\blog\designpattern_uml\mediator.png)]
-
优缺点
- 减少类间依赖,将多对多依赖转化成了一对多,降低了类间耦合
- 类间各司其职,符合迪米特法则
- 当同事类越多时,中介者就会越臃肿,变得复杂且难以维护
-
适用场景
- 系统中对象之间存在复杂的引用关系,相互产生依赖关系结构混乱且难以理解
- 交互的公共行为,如果需要改变行为,则可以增加新的中介者类
-
源码体现
- JDK中的Timer类
23、解释器模式(InterpreterPattern)(树形结构的处理)(分离实现,解释执行)
-
定义
定义一个语言的文法,并创建一个解释器去解释该语言中的句子,这里的“语言”是指使用规定格式和语法的代码
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-06b7ah6u-1692548461357)(…\Pictures\blog\designpattern_uml\interpreter.png)]
-
优缺点
- 扩展性强:在解释器模式中由于语法是由很多类表示的,当语法规则更改时,只需修改相应的非终结符表达式即可;若扩展语法时,只需添加相应的非终结符类即可
- 增加了新的解释表达式的方式
- 易于实现文法:解释器模式对应的文法应当是比较简单且易于实现的,过于复杂的语法并不适合使用解释器模式
- 语法规则较复杂时,会引起类膨胀:每个语法都要产生一个非终结符表达式
- 执行效率比较低:解释器模式采用递归调用的方法,出错时调试困难
-
适用场景
- 一些重复出现的问题可以用一种简单的语言来进行表示
- 一个简单语法需要解释的场景
-
源码体现
- JDK中Pattern对正则表达式的编译和解析
化成了一对多,降低了类间耦合 - 类间各司其职,符合迪米特法则
- 当同事类越多时,中介者就会越臃肿,变得复杂且难以维护
- JDK中Pattern对正则表达式的编译和解析
-
适用场景
- 系统中对象之间存在复杂的引用关系,相互产生依赖关系结构混乱且难以理解
- 交互的公共行为,如果需要改变行为,则可以增加新的中介者类
-
源码体现
- JDK中的Timer类
23、解释器模式(InterpreterPattern)(树形结构的处理)(分离实现,解释执行)
-
定义
定义一个语言的文法,并创建一个解释器去解释该语言中的句子,这里的“语言”是指使用规定格式和语法的代码
-
结构
[外链图片转存中…(img-06b7ah6u-1692548461357)]
-
优缺点
- 扩展性强:在解释器模式中由于语法是由很多类表示的,当语法规则更改时,只需修改相应的非终结符表达式即可;若扩展语法时,只需添加相应的非终结符类即可
- 增加了新的解释表达式的方式
- 易于实现文法:解释器模式对应的文法应当是比较简单且易于实现的,过于复杂的语法并不适合使用解释器模式
- 语法规则较复杂时,会引起类膨胀:每个语法都要产生一个非终结符表达式
- 执行效率比较低:解释器模式采用递归调用的方法,出错时调试困难
-
适用场景
- 一些重复出现的问题可以用一种简单的语言来进行表示
- 一个简单语法需要解释的场景
-
源码体现
- JDK中Pattern对正则表达式的编译和解析
- Spring中ExpressionParser接口