模板模式和适配器模式
Posted zekichen
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了模板模式和适配器模式相关的知识,希望对你有一定的参考价值。
一、模板模式
1、模板模式(Template Method pattern):指定义一个算法的骨架,并允许子类为一个或者多个步骤提供实现。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。(属于行为型模式)
2、适用场景
- 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现
- 各子类中公共的行为被提取出来并集中到一个公共的父类中,从而避免代码重复
3、优点
- 提高代码的复用性
- 提高代码的扩展性
- 符合开闭原则
4、缺点
- 类数目的增加
- 间接地增加了系统实现的复杂度
- 继承关系自身缺点,如果父类添加新的抽象方法,所有子类都要改一遍
5、应用场景举例
比如办理入职流程:填写入职登记表-->打印简历-->复印学历-->复印身份证-->签订劳动合同-->办理工牌-->安排工位。
比如平时炒菜流程:洗锅-->点火--> 热锅-->上油-->下菜-->翻炒-->放调料-->出锅。
再如赵本山问宋丹丹:“如何把大象放进冰箱?”宋丹丹答:“第一步:打开冰箱门,第二步:把大象塞进冰箱,第三步:关闭冰箱门”。赵本山再问:“怎么把长颈鹿放进冰箱?”宋丹丹答:“第一步:打开冰箱门,第二步:把大象拿出来,第三步:把长颈鹿塞进去,第四步:关闭冰箱门”,这些都是模板模式的体现。
例子1:以简单的网校课程创建流程为例:发布预习资料-->在线直播-->提交笔记-->布置作业-->检查作业。
首先定义一个NetworkCourse网课抽象类:
上面代码中有个钩子方法,主要是用来干预执行流程,使得我们控制行为流程更加灵活、更符合实际业务的需求。钩子方法的返回值一般为适合条件分支语句的返回值(如boolean、int等),可根据真实业务场景来决定是否需要用钩子方法。
接下来定义一个JavaCourse课程类:
定义一个BigDataCourse课程类:
客户端测试代码:
运行结果:
例子2:利用模板模式重构JDBC操作业务场景。
定义一个JdbcTemplate模板类,封装所有的JDBC操作。以查询为例,每次查询的表不同,返回的数据结构也不一样。我们针对不同的数据,都要封装成不同的实体对象。而每个实体封装的逻辑都是不一样的,但封装前和封装后的处理流程是不变的,因此可以用模板模式来设计这样的业务场景。 先定义一个约束ORM逻辑的接口RowMapper:
再定义一个封装了所有处理流程的抽象类JdbcTemplate:
定义一个实体类User:
定义一个数据库操作类UserDao:
客户端测试代码:
模板模式在JDK源码中的体现:先看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接口去掉,加上接口只是为了代码规范。上面的代码可以说是策略模式、简单工厂模式和适配器模式的综合运用。
简单看看适配器模式在源码中的应用,SpringMVC的HandlerAdapter类,它也有多个子类:
以上是关于模板模式和适配器模式的主要内容,如果未能解决你的问题,请参考以下文章