经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » 设计模式 » 查看文章
模板模式和适配器模式
来源:cnblogs  作者:Zeki_Chen  时间:2020/3/30 10:32:15  对本文有异议

一、模板模式

1、模板模式(Template Method pattern):指定义一个算法的骨架,并允许子类为一个或者多个步骤提供实现。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。(属于行为型模式)

2、适用场景

  • 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现
  • 各子类中公共的行为被提取出来并集中到一个公共的父类中,从而避免代码重复

3、优点

  • 提高代码的复用性
  • 提高代码的扩展性
  • 符合开闭原则

4、缺点

  • 类数目的增加
  • 间接地增加了系统实现的复杂度
  • 继承关系自身缺点,如果父类添加新的抽象方法,所有子类都要改一遍

5、应用场景举例

比如办理入职流程:填写入职登记表-->打印简历-->复印学历-->复印身份证-->签订劳动合同-->办理工牌-->安排工位。

比如平时炒菜流程:洗锅-->点火--> 热锅-->上油-->下菜-->翻炒-->放调料-->出锅。

再如赵本山问宋丹丹:“如何把大象放进冰箱?”宋丹丹答:“第一步:打开冰箱门,第二步:把大象塞进冰箱,第三步:关闭冰箱门”。赵本山再问:“怎么把长颈鹿放进冰箱?”宋丹丹答:“第一步:打开冰箱门,第二步:把大象拿出来,第三步:把长颈鹿塞进去,第四步:关闭冰箱门”,这些都是模板模式的体现。

例子1:以简单的网校课程创建流程为例:发布预习资料-->在线直播-->提交笔记-->布置作业-->检查作业。

首先定义一个NetworkCourse网课抽象类:

上面代码中有个钩子方法,主要是用来干预执行流程,使得我们控制行为流程更加灵活、更符合实际业务的需求。钩子方法的返回值一般为适合条件分支语句的返回值(如boolean、int等),可根据真实业务场景来决定是否需要用钩子方法。

接下来定义一个JavaCourse课程类:

定义一个BigDataCourse课程类:

客户端测试代码:

运行结果:

例子2:利用模板模式重构JDBC操作业务场景。

定义一个JdbcTemplate模板类,封装所有的JDBC操作。以查询为例,每次查询的表不同,返回的数据结构也不一样。我们针对不同的数据,都要封装成不同的实体对象。而每个实体封装的逻辑都是不一样的,但封装前和封装后的处理流程是不变的,因此可以用模板模式来设计这样的业务场景。 先定义一个约束ORM逻辑的接口RowMapper:

再定义一个封装了所有处理流程的抽象类JdbcTemplate:

定义一个实体类User:

定义一个数据库操作类UserDao:

客户端测试代码:

6、模板模式在源码中的应用

① 先看JDK中的AbstractList:

我们看到get()是一个抽象方法,那么它的逻辑就是交给子类来实现,ArrayList就是AbstractList的子类。同理,有AbstractList就有AbstractSet和AbstractMap,有兴趣可自行研究。还有一个每天都在用的HttpServlet,有service()、doGet()和doPost()方法,都是模板方法的抽象实现。

② Mybatis框架中的BaseExecutor类,是一个基础的SQL执行类,实现了大部分的SQL执行逻辑,然后把几个方法交给子类定制化完成,源码如下:

如doUpdate()、doQuery()等方法都交由子类来实现,如下类图:

二、适配器模式

1、适配器模式(Adapter Pattern):指将一个类的接口转换成客户期望的另一个接口,使原本接口不兼容的类可以一起工作。(属于结构型设计模式)

2、适用场景

  • 已经存在的类,它的方法和需求不匹配(方法结果相同或相似)的情况
  •  适配器模式不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品、不同厂家造成功能类似而接口不相同情况下的解决方案(有点亡羊补牢的感觉)

3、优点

  • 能提高类的透明性和复用,现有的类复用但不需要改变
  • 目标类和适配器类解耦,提高程序的扩展性
  • 在很多业务场景中符合开闭原则

4、缺点

  • 适配器编写过程需要全面考虑,可能会增加系统的复杂性
  • 增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱

5、应用场景举例

比如电源插转换头、手机充电转换头、显示器转接头。

例子1:在中国民用电都是220V交流电,但我们手机使用的锂电池使用5V直流电。因此,我们给手机充电时就需要使用电源适配器来进行转换。下面代码还原场景:

定义一个AC220类,表示220V交流电:

定义一个DC5接口,表示5V直流电的标准:

定义一个电源适配器PowerAdapter类:

客户端测试代码:

上面的案例中,通过增加PowerAdapter电源适配器,实现了二者的兼容。

例子2:重构第三方登录自由适配的业务场景。

以前开发的老系统应该都有登录接口, 但随着业务的发展和社会的进步,单纯地依赖用户名密码登录显示不能满足用户需求了。现在,我们大部分系统都已经支持多种登录方式,如QQ登录、微信登录、手机登录等,同时保留用户名密码的登录方式。虽然登录形式丰富了,但登录后的处理逻辑可不必改变,同样是将登录状态保存到session,遵循开闭原则。

首先定义统一的返回结果Result类:

假设老系统的登录逻辑SignInService:

为了遵循开闭原则,老系统的代码我们不去修改。下面开始重构,先定义User类:

定义一个第三方登录新类SignInForThirdService继承原来的逻辑,运行非常稳定的代码我们不去改动:

客户端测试代码:

通过这么一个简单的适配,完成了代码兼容。当然,代码还可以更加优雅,根据不同的登录方式,创建不同的Adapter。

首先,定义一个LoginAdapter接口:

分别实现不同的登录适配,QQ登录LoginForQQAdapter类:

微信登录LoginForWechatAdapter类:

然后定义第三方登录兼容接口IPassportForThird:

实现兼容PassportForThirdAdapter类:

客户端测试代码:

类结构图:

至此,我们在遵循开闭原则的前提下,完整地实现了一个兼容多平台登录的业务场景。适配器模式主要解决的是功能兼容问题,且适配器的实现逻辑并不依赖于接口,我们完全可以将LoginAdapter接口去掉,加上接口只是为了代码规范。上面的代码可以说是策略模式、简单工厂模式和适配器模式的综合运用。

6、适配器模式在源码中的应用

简单看看适配器模式在源码中的应用,SpringMVC的HandlerAdapter类,它也有多个子类:

原文链接:http://www.cnblogs.com/ZekiChen/p/12571428.html

 友情链接: NPS