模板方法实践

模板,方法,实践 · 浏览次数 : 276

小编点评

**设计模式:模板方法** **目的:** * 减少重复代码 * 提高代码可维护性 * 促进开发人员快速创建测试方案 **主要特征:** * 定义一个上层接口 `Event`,包含所有测试逻辑的抽象方法。 * 定义一个抽象类 `AbstractJobDefine`,其中包含 `run` 方法的抽象方法。 * 每个子类继承 `AbstractJobDefine`,实现具体的测试逻辑。 * 使用 `CompletableFuture` 和 `thenAccept` 等方法来异步执行子类实现的逻辑。 * 在 `start` 方法中,使用 `allOf` 方法并发执行多个子类的逻辑。 **示例:** ```java // Event 接口 public interface Event { void addJob(); void finishOne(String jobName, String finishCost); } // AbstractJobDefine 接口 public abstract class AbstractJobDefine { private Event event; public AbstractJobDefine(Event event) { this.event = event; } public abstract void run(Client client) throws Exception; } // Test1 类实现 AbstractJobDefine public class Test1 extends AbstractJobDefine { @Override public void run(Client client) throws Exception { // 测试1的逻辑 } } // Test2 类实现 AbstractJobDefine public class Test2 extends AbstractJobDefine { @Override public void run(Client client) throws Exception { // 测试2的逻辑 } } ``` **优点:** * 简化测试逻辑 * 提高代码可维护性 * 促进并发执行 **缺点:** * 需要为每个测试创建独立的子类 * 难以管理和调试多个测试

正文

前言

最近在设计一个对某个中间件的测试方案,这个测试方案需要包含不同的测试逻辑,但相同的是需要对各个环节进行记录;比如统计耗时、调用通知 API 等相同的逻辑。

如果每个测试都单独写这些逻辑那无疑是做了许多重复工作了。

基于以上的特征很容易能想到模板方法这个设计模式。

这是一种有上层定义框架,下层提供不同实现的设计模式。

比如装修房子的时候业主可以按照自己的喜好对不同的房间进行装修,但是整体的户型图不能做修改,比如承重墙是肯定不能打的。

而这些固定好的条条框框就是上层框架给的约束,下层不同的实现就有业主自己决定;所以对于整栋楼来说框架都是固定好的,让业主在有限的范围内自由发挥也方便物业的管理。

具体实现

以我这个案例的背景为例,首先需要定义出上层框架:

Java

Event 接口:

public interface Event {

    /**
     * 新增一个任务
     */
    void addJob();

    /**
     * 单个任务执行完毕
     *
     * @param jobName    任务名称
     * @param finishCost 任务完成耗时
     */
    void finishOne(String jobName, String finishCost);

    /**单个任务执行异常
     * @param jobDefine 任务
     * @param e 异常
     */
    void oneException(AbstractJobDefine jobDefine, Exception e);

    /**
     * 所有任务执行完毕
     */
    void finishAll();
}
    public void start() {
        event.addJob();
        try {
            CompletableFuture.runAsync(() -> {
                StopWatch watch = new StopWatch();
                try {
                    watch.start(jobName);
                    // 不同的子业务实现
                    run(client);
                } catch (Exception e) {
                    event.oneException(this, e);
                } finally {
                    watch.stop();
                    event.finishOne(jobName, StrUtil.format("cost: {}s", watch.getTotalTimeSeconds()));
                }
            }, TestCase.EXECUTOR).get(timeout, TimeUnit.SECONDS);
        } catch (Exception e) {
            event.oneException(this, e);
        }
    }

    /** Run busy code
     * @param client
     * @throws Exception e
     */
    public abstract void run(Client client) throws Exception;    

其中最核心的就是 run 函数,它是一个抽象函数,具体实现交由子类完成;这样不同的测试用例之间也互不干扰,同时整体的流程完全相同:

  • 记录任务数量
  • 统计耗时
  • 异常记录

等流程。


接下来看看如何使用:

        AbstractJobDefine job1 = new Test1(event, "测试1", client, 10);
        CompletableFuture<Void> c1 = CompletableFuture.runAsync(job1::start, EXECUTOR);

        AbstractJobDefine job2 = new Test2(event, "测试2", client, 10);
        CompletableFuture<Void> c2 = CompletableFuture.runAsync(job2::start, EXECUTOR);

        AbstractJobDefine job3 = new Test3(event, "测试3", client, 20);
        CompletableFuture<Void> c3 = CompletableFuture.runAsync(job3::start, EXECUTOR);

        CompletableFuture<Void> all = CompletableFuture.allOf(c1, c2, c3);
        all.whenComplete((___, __) -> {
            event.finishAll();
            client.close();
        }).get();

显而易见 Test1~3 都继承了 AbstractJobDefine 同时实现了其中的 run 函数,使用的时候只需要创建不同的实例等待他们都执行完成即可。

以前在 Java 中也有不同的应用:

https://crossoverjie.top/2019/03/01/algorithm/consistent-hash/?highlight=%E6%A8%A1%E6%9D%BF%E6%96%B9%E6%B3%95#%E6%A8%A1%E6%9D%BF%E6%96%B9%E6%B3%95

Go

同样的示例用 Go 自然也可以实现:

func TestJobDefine_start(t *testing.T) {
	event := NewEvent()
	j1 := &JobDefine{
		Event:   event,
		Run:     &run1{},
		JobName: "job1",
		Param1:  "p1",
		Param2:  "p2",
	}
	j2 := &JobDefine{
		Event:   event,
		Run:     &run2{},
		JobName: "job2",
		Param1:  "p11",
		Param2:  "p22",
	}
	j1.Start()
	j2.Start()
	for _, ch := range event.GetChan() {
		<-ch
	}
	log.Println("finish all")

}

func (r *run2) Run(param1, param2 string) error {
	log.Printf("run3 param1:%s, param2:%s", param1, param2)
	time.Sleep(time.Second * 3)
	return errors.New("test err")
}

func (r *run1) Run(param1, param2 string) error {
	log.Printf("run1 param1:%s, param2:%s", param1, param2)
	return nil
}

使用起来也与 Java 类似,创建不同的实例;最后等待所有的任务执行完毕。

总结

设计模式往往是对某些共性能力的抽象,但也没有一个设计模式可以适用于所有的场景;需要对不同的需求选择不同的设计模式。

至于在工作中如何进行正确的选择,那就需要自己日常的积累了;比如多去了解不同的设计模式对于的场景,或者多去阅读优秀的代码,Java 中的 InputStream/Reader/Writer 这类 IO 相关的类都有具体的应用。

与模板方法实践相似的内容:

模板方法实践

前言 最近在设计一个对某个中间件的测试方案,这个测试方案需要包含不同的测试逻辑,但相同的是需要对各个环节进行记录;比如统计耗时、调用通知 API 等相同的逻辑。 如果每个测试都单独写这些逻辑那无疑是做了许多重复工作了。 基于以上的特征很容易能想到模板方法这个设计模式。 这是一种有上层定义框架,下层提

软件设计模式系列之二十四——模板方法模式

在软件设计领域,设计模式是一组被反复使用、多次实践验证的经典问题解决方案。其中,模板方法模式是一种行为型设计模式,用于定义一个算法的骨架,将算法中的某些步骤延迟到子类中实现,从而使子类可以重新定义算法的某些特定步骤,同时保持算法的整体结构不变。本文将深入探讨模板方法模式,包括其定义、举例、结构、实现...

实践Pytorch中的模型剪枝方法

摘要:所谓模型剪枝,其实是一种从神经网络中移除"不必要"权重或偏差的模型压缩技术。 本文分享自华为云社区《模型压缩-pytorch 中的模型剪枝方法实践》,作者:嵌入式视觉。 一,剪枝分类 所谓模型剪枝,其实是一种从神经网络中移除"不必要"权重或偏差(weigths/bias)的模型压缩技术。关于什

“事后达尔文”—— 游戏业务效果评估方法实践

本文介绍了互联网业务数据效果评估的几种常见问题及方法,并基于分层抽样的逻辑优化出一套可应用于解决用户不均匀的“事后达尔文"分析法,可适用于无法ab测试或人群不均匀的ab测试等场景下的效果评估中,本文会基于实际应用案例,来给大家仔细阐述相关方法模型的思考过程,实现原理,应用结果,希望能够帮助大家,如果能对大家在各自领域中的业务效果评估有所助益的话,那就更棒了!

设计模式之模板方法模式

# 一、简介 模板方法模式是一种行为型设计模式,它定义一个操作(模板方法)的基本组合与控制流程,将一些步骤(抽象方法)推迟到子类中,在使用时调用不同的子类,就可以达到不改变一个操作的基本流程情况下,即可修改其中的某些特定步骤。这种设计方式将特定步骤的具体实现与操作流程分离开来,实现了代码的复用和扩展

设计模式学习(十四):模板方法

设计模式学习(十四):模板方法 作者:Grey 原文地址: 博客园:设计模式学习(十四):模板方法 CSDN:设计模式学习(十四):模板方法 模板方法 模板方法是一种行为型模式。 假设我们要实现一个游戏,这个游戏有「初始化」,「启动」,「结束」三个方法,那么可以定义一个游戏的模板: public a

万字详解常用设计模式

本文是博主在工作中对常用设计模式的使用经验总结归纳而来分享给大家。 设计模式一共有23种,本文讲解涉及如下: 责任链模式 模板方法模式 发布订阅模式 策略模式 三大分类 业界一般将设计模式分为三大类: 创建型模式:对类的实例化过程进行了抽象,能够将软件模块中对象的创建和对象的使用分离。有五种创建型模

提前预体验阿里大模型“通义千问”的方法来了!

随着AI大模型的浪潮席卷全球,如今的AI技术已经颠覆了大家对传统AI的认识,微软更是用浏览器与搜索引擎上的实践,证明了当今的AI技术具备打破行业格局的能力。 对于我们应用开发者来说,AI基建的建设与竞争是无法参与的,但在AI的应用领域依然大有可为!目前,国内各大科技公司已经陆续推出了各自的AI大模型

AB实验遇到用户不均匀怎么办?—— vivo游戏中心业务实践经验分享

本文会基于实际应用案例,来给大家仔细阐述AB实验相关方法模型的思考过程,实现原理,应用结果,希望能够帮助大家在各自领域中解决用户不均匀问题时带来参考和启发。

我为什么还要造一个前端轮子?

一、原因 现在市面上有很多为前端开发的框架、模板。为什么我们还要再做一个呢,究其原因,因为这些框架和模板更多的基于技术层面提供了快捷方便的实现方法;但却缺少具体业务层面的实现。因此,结合自身需求,将各种系统常用的功能和页面进行归纳总结,实现了一套更贴近实际业务的前端框架,框架中提供了一系列实际的业务