GOF23设计模式-结构型(7种)

醉酒当歌 提交于 2019-11-27 10:51:28


源码链接:https://github.com/ouyangxizhu/design_pattern.git
在这里插入图片描述

二、结构型模式

1. 外观模式(门面模式)

com.ouyangxizhu.design.pattern.structural.facade

定义

提供了一个结构来访问子系统中的一群接口。
这个外观结构封装了子系统之间的调用逻辑,调用顺序。比如购物,用户只关注我能不能买成功(下单功能),至于怎么减库存,怎么生成订单,怎么物流配送都不需要知道。

适用场景

  1. 当子系统越来越复杂,增加外观模式提供简单接口调用。
  2. 构件多层系统结构,利用外观对象作为每层的入口,简化层间调用。

优点

  1. 简化了调用过程,无需深入了解子系统,防止带来风险。
  2. 减少系统依赖,松散耦合。
  3. 更好的划分访问层次。
  4. 符合迪米特法则,即最少知道原则。(调用者只和外观类有关系)

缺点

  1. 增加或者扩展子系统容易引入风险。
  2. 增加或者扩展子系统不符合开闭原则。(可以使用抽象外观类,或者采用继承实现)

源码

都是对jdbc的封装。
org.springframework.jdbc.support.JdbcUtils#closeConnection(封装Connection的close()等)
org.springframework.jdbc.support.JdbcUtils#getResultSetValue(java.sql.ResultSet, int, java.lang.Class<?>)
org.apache.ibatis.session.Configuration#newParameterHandler等handler
tomcat的requestFacade类(实现HttpSevletRequest接口)StatementFacade等

外观模式和中介者模式

外观模式关注外界和外观类(封装子系统)之间的交互。
中介者模式关注子系统内部之间的交互。

外观模式和单例模式

可以把外观模式的外观对象做成单例使用。

外观模式和抽象工厂模式

外观对象可以通过抽象工厂获得子系统的实例。子系统可以对外观实例屏蔽细节。

2. 装饰者模式

com.ouyangxizhu.design.pattern.structural.decorator

定义

装饰者有一个有参构造器,该参数为被装饰者,只需要在调用被装饰者前或者后添加逻辑,就可以进行装饰。可以多层装饰。
在不改变原有对象的基础上,将功能附加到对象上。提供了比继承更有弹性的替代方案(扩展原有对象功能,装饰者是运行期间添加,并且可以自由组合顺序的装饰,继承是编译过程添加的。装饰者会继承被装饰的对象)一般装饰者会有自己父类的属性,可以在构造器的时候注入。

适用场景

  1. 添加一个类的功能,或者给一个类添加附加职责。
  2. 动态的给一个对象添加功能,这些功能可以再动态撤销

优点

  1. 继承的有力补充,比继承灵活,不改变原有对象的基础上给对象扩展功能。
  2. 通过使用不同的装饰类以及这些装饰类的排列组合,可以实现不同的效果。
  3. 符合开闭原则。

缺点

  1. 会出现更多的代码,更多的类,增加程序的复杂性。
  2. 动态装饰,多层装饰时会更加复杂。排查会比较困难。

源码

java.io.BufferedReader#BufferedReader(java.io.Reader, int)
java.io.BufferedInputStream#BufferedInputStream(java.io.InputStream, int)
java.io.FileInputStream#FileInputStream(java.io.File)
org.springframework.cache.transaction.TransactionAwareCacheDecorator
org.springframework.session.web.http.SessionRepositoryFilter.SessionRepositoryRequestWrapper
org.apache.ibatis.cache.decorators

装饰者模式和代理模式

装饰者模式关注在一个对象上动态的添加方法,代理模式关注对对象的控制访问。
代理对象可以对客户隐藏对象的具体信息,使用代理类的时候会创建被代理对象的实例,装饰者模式通常将被装饰者作为参数传给装饰者的构造器。
装饰者模式和代理模式都可以实现前置和后置通知,但是装饰者不能环绕通知,代理模式可以。

装饰者和适配器模式

都是wrapper,装饰者和被装饰者实现相同的接口,或者装饰者是被装饰者的子类,适配器一般是实现不同的接口。

3. 适配器模式

com.ouyangxizhu.design.pattern.structural.adapter

定义

将一个接口转换为客户需要的另一个接口。是原本接口不兼容的类可以一起工作。

适用场景

  1. 已经存在的类,他的方法和需求不匹配时(方法结果相同或相似)。
  2. 不是软件设计阶段考虑的设计模式,是随着软件的维护,由于不同产品,不同厂家,造成功能类似为接口不同情况下的解决方案。

优点

  1. 能提高类的透明性和复用,现有的类复用但不需要改变。
  2. 目标类和适配器类解耦,提高程序扩展性。
  3. 符合开闭原则。

缺点

  1. 适配器编写过程需要全面考虑,可能会增加系统的复杂性。
  2. 增加系统代码的可读难度。

扩展

对象适配器模式,符合组合复用原则。使用委托机制。适配器类包含被适配者的属性。(组合)
类适配器模式,使用类继承

源码

java.io.InputStreamReader
javax.xml.bind.annotation.adapters
org.springframework.aop.framework.adapter.MethodBeforeAdviceAdapter
org.springframework.web.servlet.HandlerAdapter
org.springframework.web.servlet.DispatcherServlet#initHandlerAdapters

适配器模式和外观模式

外观模式是定义了一个新的接口,适配器模式是复用原有的接口。外观的规模更大。

4. 享元模式

com.ouyangxizhu.design.pattern.structural.flyweight

定义

提供了减少对象数量,从而改善应用所需的对象结构的方式。应用共享技术有效的支持大量细粒度的对象。

适用场景

  1. 常常应用于系统的底层开发,以解决系统的性能问题。
  2. 系统有大量的相似对象,需要缓冲池的场景。

优点

  1. 减少对象创建,降低内存中的对象数量,降低系统的内存,提高效率。
  2. 减少内存之外的其他占用。比如new对象需要一定的时间。

缺点

  1. 区别内部和外部状态,关注线程安全问题。
  2. 使程序系统的逻辑复杂化。

内部状态和外部状态

内部状态指在享元对象的内部,不会随着环境变化的共享部分。
外部状态指在享元对象的外部,会随着环境变化,不能共享。

源码

字符串常量池,连接池。方法区中。文件句柄,窗口句柄。Integer的缓存。
org.apache.ibatis.binding.MapperProxy#cachedMapperMethod

享元模式和代理模式

可以用享元模式来代替代理里面创建的对象。

享元模式和单例模式

IOC容器就是两者结合的例子。

5. 组合模式

com.ouyangxizhu.design.pattern.structural.composite

定义

将对象组合成树形结构以表示”部分-整体“的层次结构。组合模式使客户端都单个对象和组合对象保持一致的方式处理。(部分和整体继承同一个类或实现相同的接口(一致的对象类型,传入的参数是父类)对整体和部分进行定制化处理(在各自对应的类中))

适用场景

  1. 希望客户端可以忽略组合对象和单个对象的差异时。
  2. 处理一个树形结构。

优点

  1. 清楚地定义分层次的复杂对象,表示对象的全部或者部分层次。
  2. 让客户端忽略了层次的差异,方便对整个层次结构进行控制。简化客户端代码。(要不然客户端得判断类型)
  3. 符合开闭原则。

缺点

  1. 限制类型会比较复杂。
  2. 让设计变的更加抽象。

源码

java.util.ArrayList#addAll(java.util.Collection<? extends E>)注意传入的参数是父类对象。
org.apache.ibatis.scripting.xmltags.SqlNode及其实现类。(各种sql都解析成SqlNode)

组合模式和访问者模式

可以用访问者模式访问组合模式的递归结构。

6. 桥接模式

com.ouyangxizhu.design.pattern.structural.bridge
在这里插入图片描述

定义

将抽象部分与他的具体实现部分分离,使他们都可以独立的变化(通过组合方式建立两者之间的关系,而不是继承)将继承关系变为关联关系。相应的方法一定要委托给被组合的对象

适用场景

  1. 抽象和具体实现之间增加更多的灵活性
  2. 一个类存在两个或者多个独立变化的维度。且这两个或者多个维度都需要进行独立的扩展。
  3. 不希望继承,继承会使类的个数增加。

优点

  1. 分离抽象部分和具体实现部分。一定程度可以避免子类剧烈增加的后果。
  2. 提高系统可以扩展性。
  3. 符合开闭原则
  4. 符合合成复用原则。

缺点

  1. 增加系统的理解和设计难度。
  2. 需要正确的识别出系统中两个独立变化的维度。

源码

java.sql.DriverManager和java.sql.Driver。
java.sql.DriverManager#getConnection(java.lang.String, java.util.Properties)的java.sql.Connection 可以在不同数据库中转化。

桥接和组合模式

桥接模式注重平行级别间类直接的组合。组合模式强调部分和整体间的组合。

桥接模式和适配器模式

适配器改变接口,桥接模式把类的抽象和具体实现分离开。

7. 代理模式

com.ouyangxizhu.design.pattern.structural.proxy

定义

为其他对象提供一种代理,以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介的作用。

适用场景

  1. 保护目标对象
  2. 增强目标对象

优点

  1. 可以将代理对象和被调用的目标对象分离
  2. 一定程度上降低耦合度,扩展性好
  3. 保护目标对象
  4. 增强目标对象

缺点

  1. 造成系统设计中类的数目增加(代理类)
  2. 有代理类,会造成请求处理的速度变慢
  3. 增加系统复杂度。

静态代理和动态代理

静态代理在编译阶段确定被代理的类,JDK动态代理代理接口。CGLIB针对类(生成被代理类的子类,重写方法,所以final的不能被代理)速度:JDK动态代理>CGLIB。动态代理是运行时确定的,自己传的,因此代码可以复用。

源码

org.springframework.aop.framework.ProxyFactoryBean(核心方法getObject())。
org.springframework.aop.framework.JdkDynamicAopProxy
org.apache.ibatis.binding.MapperProxyFactory
org.apache.ibatis.binding.MapperProxy
其实mybatis的dao接口就是通过代理模式实现调用的

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!