Su的技术博客

  • 首页
  • 原创
  • 视频
  • Java
  • MySQL
  • DDD
  • 事故复盘
  • 架构方案
  • AI
  • Other
  • 工具
    • AI工具集
    • 工具清单
    • JSON在线格式化
    • JSON在线比较
    • SQL在线格式化
  • 打赏
  • 关于
路很长,又很短
  1. 首页
  2. Java
  3. 正文
                           

【淘宝】浅析设计模式4——模板方法模式

2023-06-13 1095点热度 0人点赞 0条评论

我们在进行软件开发时要想实现可维护、可扩展,就需要尽量复用代码,并且降低代码的耦合度。设计模式就是一种可以提高代码可复用性、可维护性、可扩展性以及可读性的解决方案。大家熟知的23种设计模式,可以分为创建型模式、结构型模式和行为型模式三大类。本专题着眼于实际开发过程中常用的几种设计模式,从理论和实战两个角度进行讨论和分享,力求逻辑清晰、表述简洁,帮助大家在项目中合理运用设计模式,保障代码的可靠性。

本文为此系列第四篇文章,前三篇见——

第一篇:浅析设计模式1——工厂模式

第二篇:浅析设计模式2——策略模式

第三篇:浅析设计模式3——装饰者模式

 

概述

我们在进行软件开发时要想实现可维护、可扩展,就需要尽量复用代码,并且降低代码的耦合度,而设计模式就是一种可以提高代码可复用性、可维护性、可扩展性以及可读性的解决方案。
浅析设计模式4——模板方法模式

大家熟知的23种设计模式,可以分为创建型模式、结构型模式和行为型模式三大类。其中,行为型模式可用于描述程序中多个类和多个对象如何协作完成复杂的任务,涉及不同对象间的职责分配、算法的抽象化。模版方法模式是一种应用广泛的行为型模式,本文将着眼于模版方法模式进行学习分享,阐述模板方法模式的核心概念和应用实践。认识和使用模板方法模式,可以帮助我们在设计代码时更好的控制算法流程,避免大量重复代码,从而有效提升编程效率。

浅析设计模式4——模板方法模式

基本概念

模版方法模式的核心思想是:首先在抽象类中定义一个任务的算法骨架,将算法的执行细节延迟到子类中个性化实现。注意,子类可以在不改变算法架构的情况下,重新定义特定步骤,甚至干预算法的执行流程,从而起到控制抽象父类行为的作用。
下面从模式结构和使用步骤两个层面,简单阐述模版方法模式的基本概念。
▐结构

模版方法模式的结构相对简单,主要包含两大类:抽象类、具体类,抽象父类中首先定义好算法流程,具体的步骤细节延迟到具体子类中执行。

角色 关系 作用
抽象类 Abstract Class 具体类的父类 定义一个抽象的模版类,给出算法的骨架,包含一个模版方法和若干个基本方法(抽象方法、具体方法、钩子方法)
具体类 Concrete Class 抽象构件的接口实现类 定义一个具体的实现类,实现抽象类中定义的抽象方法和钩子方法。

浅析设计模式4——模板方法模式

▐使用

有了上述的基本概念,我们将装饰者模式的使用步骤概括为:

step1:创建抽象类,定义一个算法骨架,包含一个模版方法和若干个基本方法:

模版方法:算法流程,定义基本方法的执行顺序;

基本方法 - 具体方法:在抽象类中实现,可被具体类继承或重写;

基本方法 - 抽象方法:在抽象类中声明,由具体类实现;

基本方法 - 钩子方法:在抽象类中实现,包含一种用于判断的方法、一种由具体类重写的空方法。

step2:创建具体类,实现抽象类中定义的抽象方法和钩子方法;

 

使用示例

这里还是以网购为例,简单阐述如何使用模版方法模式。我们在网购时,基本流程都是先选择一个商品、确定样式或型号、下单支付(也可能会和购物车其它商品一起支付)、收货(如果不满意还可退换货)、终止一单网购交易。当然这个过程可能还会掺杂多种其它情况,我们将购物流程简化为:选择商品、下单支付、收货(退换货)、终止交易,实现如下。
▐代码实现
// 创建抽象父类,定义算法骨架
public abstract class TMAbstractClass {

    public final void shopOnline() {
        selectItems();
        checkoutItems();
        if(isReturnItemsHook1()) {
            returnItems();
        } else if(isExchangeItemsHook2()) {
            exchangeItems();
            //假设一次性换到满意的商品
            confirmTheReceipt();
        } else {
            confirmTheReceipt();
        }
        terminateTransaction();
    }

    protected abstract void selectItems();

    protected void checkoutItems() {
        System.out.println("下单支付");
    }

    public boolean isReturnItemsHook1() {
        return false;
    }
    protected void returnItems() {
    }

    public boolean isExchangeItemsHook2() {
        return false;
    }
    protected void exchangeItems() {
    }

    protected void confirmTheReceipt() {
        System.out.println("确认收货");
    }

    protected void terminateTransaction() {
        System.out.println("终止交易");
    }

}

// 定义具体子类1:购买商品1
public class TMConcreteClass1 extends TMAbstractClass{

    @Override
    protected void selectItems() {
        System.out.println("选择商品1");
    }

    @Override
    public boolean isReturnItemsHook1() {
        return true;
    }

    @Override
    protected void returnItems() {
        System.out.println("退货退款");
    }

}

// 定义具体子类2:购买商品2
public class TMConcreteClass2 extends TMAbstractClass{

    @Override
    protected void selectItems() {
        System.out.println("选择商品2");
    }

    @Override
    public boolean isExchangeItemsHook2() {
        return true;
    }

    @Override
    protected void exchangeItems() {
        System.out.println("换货");
    }
}

//客户端调用
public class userPayForItem() {
    public static void main(String[] args) {
        System.out.println("购物记录1:");
        TMAbstractClass shopRecord1 = new TMConcreteClass1();
        shopRecord1.shopOnline();
        System.out.println();
        System.out.println("购物记录2:");
        TMConcreteClass2 shopRecord2 = new TMConcreteClass2();
        shopRecord2.shopOnline();      
    }
}
▐结果输出
购物记录1:
选择商品1
下单支付
退货退款
终止交易

购物记录2:
选择商品2
下单支付
换货
确认收货
终止交易
▐UML图

浅析设计模式4——模板方法模式

 

JDK源码赏析

模板方法模式在框架源码中使用也很广泛,比如:JDK 源码中 AbstractList 抽象类、Mybatis 源码中 BaseExecutor 抽象类。本文以 AbstractList 为例,分析模版方法模式在源码中如何应用。AbstractList 是 ArrayList 的父类,也就是模版类,它包含的方法有很多,这里主要介绍一下 addAll() 方法。

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
    public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);
        boolean modified = false;
        for (E e : c) {
            add(index++, e);
            modified = true;
        }
        return modified;
    }
}

事实上,AbstractList 中的方法除了一些私有方法不能被子类访问,大多数方法都和 addAll() 一样,可由子类选择是否修改:如果子类需要做个性化的实现就要修改,如果不需要则直接按照父类方法的逻辑执行。

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount

        int numMoved = size - index;
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);

        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }
}
另外,AbstractList 中有一个 get() 方法,明确要求子类修改实现,如下所示:
rivate final AbstractList<E> l;
private final int offset;

abstract public E get(int index);

public E get(int index) {
        rangeCheck(index);
        checkForComodification();
        return l.get(index+offset);
}

左边是父类AbstractList中的,右边是ArrayList中的方法。在父类中没有直接写出实现代码,而是让子类自己手动去实现。除此之外其实还有一个方法就是AbstractList父类AbstractCollection中的toString方法。在ArrayList中是没有的,但是平常在写代码时候,是可以直接调用的,这就是一个公共的方法。

 

优缺点及适用场景

▐优点
  1. 封装不变部分, 扩展可变部分。把认为是不变部分的算法封装到父类实现, 而可变部分的则可以通过继承来继续扩展。
  2. 提取公共部分代码, 便于维护。
  3. 行为由父类控制, 子类实现。基本方法是由子类实现的, 因此子类可以通过扩展的方式增加相应的功能, 符合开闭原则。
▐缺点
  1. 子类执行结果影响父类结果,这违背了我们平时设计代码的习惯,在复杂项目中,很可能会带来阅读上的难度。
  2. 可能引起子类泛滥、为了继承而继承的问题。
▐适用场景
  1. 算法的整体步骤相对固定、其中个别方法容易变化时,这时候可以使用模板方法模式,将易变部分抽象出来,供子类实现。
  2. 当多个子类存在公共行为时,可以将其提取出来并集中到一个公共父类中以避免代码重复。
  3. 当需要控制子类的扩展时,模板方法只在特定点调用钩子操作,即可进行个性化的功能扩展。

     

总结

模板方法模式只需要简单的继承关系就可以完成,是一种比较简单易用的设计模式。我们在平常写代码时,很可能也会经常使用模板方法模式。根据上文所述,如果我们希望子类不要修改父类的方法,只需要加上 final 修饰即可;如果希望子类一定重写父类的方法,就将父类的方法用 abstract 修饰;如果子类可以修改也可以不修改,就可以参照 addAll() 方法那样设计。模板方法的重点是要理解模板,这个模板需要尽量使用抽象类。因为抽象类比接口更加灵活,能更好得定义模板。
 

 

本文仅供学习!所有权归属原作者。侵删!文章来源: 大淘宝技术

更多文章:

  1. 浅析设计模式1 —— 工厂模式
  2. 浅析设计模式2 —— 策略模式
  3. 浅析设计模式3 —— 装饰者模式
  4. 浅析设计模式5 -- 责任链模式
  5. 责任链模式在复杂数据处理场景中的实战
  6. 设计模式在外卖营销业务中的实践
  7. 记一次堆内外内存问题的排查和优化
  8. 【进阶玩法】策略+责任链+组合实现合同签章
  9. ParNew+CMS 实践案例 (一)- NameNode YGC诊断优化
  10. 万字长文全面了解学习Netty!
标签: 淘宝 Java 模板方法模式 设计模式
最后更新:2023-06-11

coder

分享干货文章,学习先进经验。

打赏 点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

广告
文章目录
  • 概述
  • 基本概念
  • 使用示例
  • JDK源码赏析
  • 优缺点及适用场景
  • 总结
最新 热点 推荐
最新 热点 推荐
视频笔记:微服务架构P4 设计模式:每服务数据库、API 网关和事件驱动架构 干货 | 论Elasticsearch数据建模的重要性 马蜂窝消息总线——面向业务的消息服务设计 基于 MySQL Binlog 实现可配置的异构数据同步 视频笔记:Google发布Agent2Agent协议 视频笔记:什么是微服务,为什么是微服务? 视频笔记:什么是AI 智能体? 视频笔记:什么是Flink?
Elasticsearch 使用误区之六——富文本内容写入前不清洗基于 MySQL Binlog 实现可配置的异构数据同步马蜂窝消息总线——面向业务的消息服务设计视频笔记:微服务架构P4 设计模式:每服务数据库、API 网关和事件驱动架构干货 | 论Elasticsearch数据建模的重要性你可以不用RxJava,但必须得领悟它的思想!如何秒级实现接口间“幂等”补偿:一款轻量级仿幂等数据校正处理辅助工具视频笔记:什么是Flink?
手把手教你落地DDD Spring中@Autowired和@Inject注解的区别? Log4j框架疯狂写日志,导致磁盘打满问题排查 Lombok 同时使用 @Data 和 @Builder 的巨坑,千万别乱用! 微博话题高性能降级设计-final.pdf Codeium:强大且免费的AI智能编程助手 详解微服务应用灰度发布最佳实践 2.软件架构预述(译)

CRUD (1) Event Sourcing (1) graphql (1) id (1) NoSQL (1) quarkus (1) rest (1) RocketMQ (2) Spring Boot (1) zk (1) zookeeper (1) 上下文 (1) 事务消息 (1) 二级缓存 (1) 值对象 (1) 关系数据库 (1) 分布式缓存 (1) 原子性 (1) 唯一ID (1) 商品 (1) 多对多 (1) 子域 (1) 字符集 (1) 客户端心跳 (1) 幂等 (2) 干货 (1) 并发 (1) 应用场景 (1) 应用架构图 (1) 康威定律 (2) 异步复制 (1) 微服务架构 (3) 总体方案 (1) 技术方案 (2) 技术架构 (2) 技术架构图 (1) 技能 (1) 持续集成 (1) 支撑域 (1) 故障恢复 (1) 数据架构图 (1) 方案选型 (1) 日记 (1) 服务发现 (1) 服务治理 (1) 服务注册 (2) 机房 (1) 核心域 (1) 泄漏 (1) 洋葱架构 (1) 消息队列 (5) 源码剖析 (1) 灰度发布 (1) 熔断 (1) 生态 (1) 画图工具 (1) 研发团队 (1) 线程 (2) 组织架构 (1) 缓存架构 (1) 编码 (1) 视频 (19) 读写分离 (1) 贵州 (1) 软件设计 (1) 迁移 (1) 通用域 (1) 集群化 (1) 雪花算法 (1) 顺序消息 (1)

推荐链接🔗
  • AI工具集
  • 工具箱🛠️

COPYRIGHT © 2014-2025 verysu.com . ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

粤ICP备15033072号-2

x