• 模板模式和适配器模式


    一、模板模式

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

  • 相关阅读:
    【题解】【BT】【Leetcode】Populating Next Right Pointers in Each Node
    【题解】【BT】【Leetcode】Binary Tree Level Order Traversal
    【题解】【BST】【Leetcode】Unique Binary Search Trees
    【题解】【矩阵】【回溯】【Leetcode】Rotate Image
    【题解】【排列组合】【素数】【Leetcode】Unique Paths
    【题解】【矩阵】【回溯】【Leetcode】Unique Paths II
    【题解】【BST】【Leetcode】Validate Binary Search Tree
    【题解】【BST】【Leetcode】Convert Sorted Array to Binary Search Tree
    第 10 章 判断用户是否登录
    第 8 章 动态管理资源结合自定义登录页面
  • 原文地址:https://www.cnblogs.com/ZekiChen/p/12571428.html
Copyright © 2020-2023  润新知