设计模式(下)
- 外观模式
定义:提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。
-
- 类图
-
- 精要
- 设计原则
- 精要
-
-
- 适用场景
-
如需要一个高层接口,内部提供多种实现,则可以将这些实现的实例封装到高层接口中,对外提供统一的高层接口。
-
- 典型应用
slf4j中使用较多
- 模板方法模式
定义:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
适用场景:
-
- 类图
-
- 精要
模板方法模式,在父类中定义了模板方法,这个方法定义了一组不会变的步骤,里面的细节可以在父类就确定而实现的,也可以在子类才实现,这样做的好处是确保了算法的结构保持不变,而又可以随子类有具体的实现。
注意钩子方法
-
-
- 设计原则
-
-
-
- 适用场景
-
-
- 典型应用
- MyBatis中的模板方法模式
- 典型应用
(1)BaseExecutor
采用了模板方法模式,实现了大部分的SQL执行逻辑
(2)SimpleExecutor
每执行一次update或select,就开启一个Statement/PrepareStatement对象,用完立刻关闭。
ReuseExecutor:执行update或select,以sql作为key查找Stetement对象,存在就使用,不存在就创建,用完不关闭,而是放到Map<String,Statement>,供下一次使用。
BatchExecutor:执行update(JDBC不支持批处理select),将所有sql都添加到批处理(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement/PrepareStatement对象。
(3)BaseTypeHandler
(4)IntegerTypeHandler
-
-
- Spring中的模板方法模式
-
Spring的JDBC模板即是采用的目标方法模式
- 迭代器模式
定义:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
适用场景:
不同的类方法返回的数据集合不同,如一个返回ArrayList集合,另一个返回数组,但是需要在一个地方展示这2个数据。
迭代器让我们能够游走于聚合内部的每一个元素,而不暴露其内部的表示。
-
- 类图
《设计模式》P337
-
- 精要
-
- 相关设计原则
一个类应该只有一个引起变化的原因
-
- 典型应用
MyBatis中的迭代器模式
PropertyTokenizer类实现了Iterator接口
- 组合模式
定义:允许你将对象组合成树形结构来表现“整体/部分”层次。组合能让客户以一致的方式处理个别对象以及对象组合。
适用场景:
-
- 类图
-
- 精要
-
- 典型应用
- MyBatis中的组合模式
- 典型应用
SqlNode和各个子类ChooseSqlNode等
MyBatis支持动态sql,DynamicSqlSource.getBoundSql方法里,调用了rootSqlNode.apply(context)方法,apply()是SqlNode接口的方法,所有动态节点都实现了SqlNode接口。实现了该接口的所有节点就是整个组合模式树的各节点
- 代理模式
定义:
适用场景:
-
- 类图
-
- 精要
-
- 典型应用
- MyBatis中的代理模式
- 典型应用
JDK动态代理
MapperProxy
ConnectionLogger
Executor.loader包使用了cglib或javassist达到延迟加载的效果
MapperProxy类实现了InvocationHandler接口,并实现了invoke方法
Spring中的代理模式
Spring中代理模式最典型的应用就是AOP,实现AOP主要有2种方式,JDK和Cglib
JdkDynamicAopProxy
CglibAopProxy(Spring4之前的是Cglib2AopProxy)
- 桥接模式
桥接模式不只改变实现,还改变抽象
-
- 类图
-
- 精要
-
- 典型应用
-
- 比较
桥接模式的优点:
(1)将实现予以解耦
(2)抽象和实现可以独立扩展
(3)对于“具体的抽象类”所做的改变,不会影响客户
用途和缺点:
(1)适合使用在需要跨越多个平台的图形和窗口系统上
(2)适用在需要不同的方式改变接口和实现的场景
(3)增加了复杂度
桥接模式与策略模式比较:
个人理解:策略模式更接近于面向对象的多态,通过子类来决定具体的行为。而桥接模式在使用侧多了一层抽象,在这个抽象的实现中,在调用时决定该采用的具体桥梁。
- 生成器模式
封装一个产品的构造过程,并允许按步骤构造。
适用场景:构建对象时,如果累的参数很多,且其中很多参数类型相同并且很多参数可以为空时,适用Builder模式会更好。
-
- 类图
-
- 精要
-
- 典型应用
如参数非常多的场景,比如一个类的实例变量很多,如果在构造函数中传很多实参来创建对象,将会变得非常之复杂。
-
-
- Mybatis中的生成器模式
-
Mybatis中的Builder模式
(1)XMLConfigBuilder
XMLConfigBuilder用于读取MyBatisMapConfig.xml和所有的*Mapper.xml文件,即Mybatis的配置文件(配置了全局参数等)和SQL的配置文件(配置了SQL语句),构建MyBatis运行的核心对象Configuration。在构建Configuration对象时,还会调用XMLMapperBuilder
(2)XMLMapperBuilder
XMLMapperBuilder用于读取*Mapper文件,会使用到XMLStatementBuilder。
(3)XMLStatementBuilder
XMLStatementBuilder用于读取和build所有的SQL语句
(4)SqlSessionFactoryBuilder
SqlSessionFactoryBuilder会调用XMLConfigBuilder,Configuration对象会作为构建SqlSessionFactory的参数
(5)CacheBuilder
在这些Builder的过程中,会读取文件或配置,做大量的XpathParser解析、配置或语法的解析、反射生成对象、存入结果缓存等步骤,单纯的构造函数无法完成。
-
- 比较
生成器的优点
(1)将一个复杂对象的创建过程封装起来
(2)允许对象通过多个步骤来创建,并且可以改变过程(这和只有一个步骤的工程模式不同)
(3)向客户隐藏产品内部的表现
(4)产品的实现可以被替换,因为客户只看到一个抽象的接口
生成器的用途和缺点
(1)经常被用来创建组合结构
(2)与工厂模式相比,采用生成器模式创建对象的客户,需要具备更多的领域知识
- 责任链模式
让一个以上的对象有机会能够处理某个请求
-
- 类图
-
- 精要
-
- 典型应用
-
- 比较
责任链的优点:
(1)将请求的发送者和接受者解耦
(2)简化对象,不需要知道链的结构
(3)通过动态改变链内的成员或调动它们的次序,允许动态地新增或删除责任
用途和缺点:
(1)经常被使用在窗口系统中,处理鼠标和键盘之类的事件
(2)并不保证请求一定会被执行;如果没有任何对象处理它,可能会落到链尾之外
(3)可能不容易观察运行时的特征,有碍于除错
- 蝇量模式
让某个类的一个实例能用来提供许多“虚拟实例”。
-
- 类图
-
- 精要
-
- 典型应用
-
- 比较
蝇量的优点:
(1)减少运行时对象实例的个数,节省内存
(2)将许多“虚拟”对象的状态集中管理
用途和缺点:
(1)当一个类有许多的实例,而这些实例能被同一方法控制的时候,可以用蝇量模式
(2)一旦实现了它,那么单个的逻辑实例将无法拥有独立而不同的行为
- 解释器
为语言创建解释器
-
- 类图
-
- 精要
-
- 典型应用
-
- 比较
计时器模式的优点:
(1)将每一个语法规则表示成一个类,方便于实现语言
(2)语法由许多类表示,可以轻易地改变或扩展
(3)通过在类结构中加入新方法,可以在解释的同时增加新行为,例如打印格式的美化或进行复杂的程序验证
用途和缺点:
(1)当需要实现简单的语言时,使用解释器
(2)当有简单的语法,而且简单比效率重要时,使用解释器
(3)可以处理脚本语言和编程语言
(4)当语法规则的数目太大时,会变得非常复杂。此时,使用解析器/编译器的产生器可能更合适
- 中介者模式
集中相关对象之间复杂的沟通和控制方式
-
- 类图
-
- 精要
-
- 典型应用
-
- 比较
中介者模式的优点:
(1)将对象彼此解耦,增加对象的复用性
(2)集中控制逻辑,简化系统维护
(3)让对象之间锁传递的消息变得简单而大幅减少
用途和缺点:
(1)常常被用来协调相关的GUI组件
(2)如果设计不当,中介者对象本身会变得过于复杂
- 备忘录模式
需要让对象返回之前的状态时,使用备忘录模式
-
- 类图
-
- 精要
-
- 典型应用
-
- 比较
备忘录模式的优点:
(1)将被存储的状态放在外面,不和关键对象混在一起,帮助维护内聚
(2)保持关键对象的数据封装
(3)提供容易实现的恢复能力
用途和缺点:
(1)用于存储状态
(2)存储和灰度状态的过程肯呢个相当耗时
(3)在Java系统中,可以考虑使用序列化(serialization)机制存储系统的状态
- 原型模式
当创建给定类的实例的过程很昂贵或很复杂时,使用原型模式
-
- 类图
-
- 精要
有一个原型实例,需要基于这个实例产生新实例时,可使用原型模式
-
- 典型应用
-
- 比较
原型模式的优点:
(1)向客户隐藏制造实例的复杂性
(2)提供让客户能够产生未知类型对象的选项
(3)在某些环境下,复制对象比创建新对象更有效
用途和缺点:
(1)在一个复杂的类层次中,当系统必须从其中的许多类型创建新对象时,考虑原型模式
(2)对象的复制有时相当复杂
- 访问者模式
为一个对象的组合增加新能力,且封装不重要时,使用访问者模式
-
- 类图
-
- 精要
-
- 典型应用
-
- 比较
访问者模式的优点:
(1)允许对组合结构加入新的操作,而无需改变结构本身
(2)加入新的操作相对容易
(3)访问者所进行的操作,代码是集中在一起的
用途和缺点:
(1)采用访问者模式会打破组合类的封装
(2)游走的功能牵涉其中,对组合结构的改变更加困难
- 总结
设计模式可分为:创建型、结构型、行为型
创建型模式:工厂模式(简单工厂、抽象工厂、工厂)、单例模式、建造者模式、原型模式
创建型模式的目的是创建实例
结构型模式:代理模式、适配器模式(默认适配、对象适配、类适配)、桥接模式、装饰模式、外观模式、享元模式
行为型模式:策略模式、观察者模式、责任链模式、模板方法模式、状态模式、备忘录模式、命令模式