zoukankan      html  css  js  c++  java
  • 模板模式和适配器模式

    一、模板模式

    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类,它也有多个子类:

  • 相关阅读:
    2017NOIP游记 (格式有点炸)
    Qtree3
    [P2769] 猴子上树
    [Usaco2005 Dec]Cleaning Shifts 清理牛棚
    [NOIP2014] 解方程
    [ZJOI2012] 灾难
    [洛谷P3941] 入阵曲
    [SCOI2009] 最长距离
    [JLOI2011] 飞行路线
    虚拟化数电底层
  • 原文地址:https://www.cnblogs.com/ZekiChen/p/12571428.html
Copyright © 2011-2022 走看看