一、结构型设计模式
1、通过类或者对象的组合结构,来解决一些特定场景的问题,这类模式属于结构型设计模式。
2、结构型设计模式包括:代理模式、桥接模式、装饰器模式、适配器模式、门面模式、组合模式、享元模式
二、设计模式概述
1、代理模式
1)原理与实现
- 静态代理原理:不改变原始类的情况下,通过代理类的方式,实现附加功能的扩展。如果原始类有定义接口,通过实现相同的接口,来替换原始类;如果原始类没有定义接口,则通过继承原始类的方式进行扩展(super.invoke)。
- 静态代理的缺点:代理类个数=原始类个数,增加类的同时,增加维护成本。
- 动态代理原理:在运行时,通过动态创建代理的方式替换原始类,例如Spring AOP底层实现。
2)场景
非功能性需求:监控、日志、限流、鉴权、事务等,在原始类基础上附加功能,不影响原有逻辑。
3)实例:Spring AOP底层实现(动态代理的实现)
2、桥接模式(不常用)
1)原理与实现
原理:将抽象与实现解耦,让他们可以独立变化。(有点像基于接口而非实现编程或组合代替继承)
理解的关键在于,“抽象”与“实现”的理解。一个定义结构,一个落地实现,通过组合的方式来实现。
2)场景
JDBC规范和不同Driver的实现。
3)实例:Java JDBC与Mysql Driver/Oracle Driver
3、装饰器模式
1)原理与实现
- 原理:用组合代替继承的结构,实现原始类相关功能的增强。(比如原始IO类基础上增加缓存)
- 特点:与原始类实现或继承相同的接口(父类),便于嵌套多个装饰器;目的在于功能增强,这一点是与代理、桥接模式最大的区别。
2)场景
主要场景,解决继承关系过于复杂的问题,用组合结构代替继承。主要作用,对原有功能增强。如果原始类需要嵌套多个装饰器,需要装饰器类实现或继承原始类的接口或抽象。
3)实例:Java IO类
4、适配器模式
1)原理和实现
原理:将不兼容的接口做适配,使其原本因兼容不能一起工作的接口,可以一起工作。实现方式:1、类适配器,使用继承关系+接口方式来实现;2、对象适配器,使用组合方式来实现。
2)场景
适配器模式,属于一种补偿模式。用于补救设计缺陷。具体应用场景包括:老版本接口兼容、替换外部依赖、统一多个类的接口设计、适配不同的数据格式等。
3)实例:Arrays.aslist();slf4j与log4j、logback、JUL、JCL等
5、门面模式(外观模式)
门面模式又叫外观模式,主要用于接口设计。其原理与迪米特法则、接口隔离原则类似。
1)原理:为子系统提供一组统一的接口,定义一组高级接口让子系统更易用。
2)实现:有点类似于中台架构体系中的基础服务+聚合服务的意思。应用中的某个功能需要用到中台的1、2、3、4个接口,门面模式可以将1、2、3、4组合成一个高级接口(聚合服务),提供给应用使用。
3)场景:解决易用性问题;同时由于聚合多个接口,减少网络开销解决了部分性能问题;分布式事务通过聚合服务收敛到单服务内(Spring或单库),解决其复杂性。
接口设计,在实际开发中,总会面临复用性+易用性的权衡问题。复用性太强的一般易用性都不高,易用性高的就代表定制性太强。需要在这中间寻找一个平衡点。
6、组合模式(不常用)
主要解决树形结构数据,应用场景特殊。
1)原理:将一组对象组合成一个树形结构,以表示“部分-整体”的层次结构。
2)实现:通过递归遍历算法实现。
3)场景:组织架构(部门+员工)或文件系统(文件夹+文档)
7、享元模式
1)原理:“享元”即共享单元。共享对象,以节省内存,享元对象为不可变对象。
2)实现:通过工厂模式,在工厂中的Map/List中缓存多个实例对象,来达到复用的目的。
3)实例:Java中的Integer(-128~127)、String等。
享元模式跟单例的区别:单例是全局唯一的对象,享元模式是一组类型相同的对象,即多例。
享元模式跟缓存的区别:享元侧重点在复用,缓存侧重点在提高读取效率。
三、模式间的区别
都是Wrapper二次封装原始类。最大的区别是用途。比如代理模式是监控,装饰器模式是功能增强,适配器模式与代理、装饰器最大的不同时接口不与原始类相同。
代理模式:在不改变原始类的情况下,创建一个代理类,目的是监控,而非功能增强。这是与装饰器模式最大的不同。
桥接模式:抽象与实现解耦,通过组合方式,让其各自独立变化。
装饰器模式:在不改变原始类的情况下,通过组合的方式,在原始功能上做增强。
适配器模式:将不兼容的接口,通过继承或组合的方式,使其可以一起工作。适配器模式跟原始类接口不同,而代理模式、适配器模式,与原始类接口相同。