- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
最近在设计一个对某个中间件的测试方案,这个测试方案需要包含不同的测试逻辑,但相同的是需要对各个环节进行记录;比如统计耗时、调用通知 API 等相同的逻辑.
如果每个测试都单独写这些逻辑那无疑是做了许多重复工作了.
基于以上的特征很容易能想到 模板方法 这个设计模式.
这是一种有上层定义框架,下层提供不同实现的设计模式.
比如装修房子的时候业主可以按照自己的喜好对不同的房间进行装修,但是整体的户型图不能做修改,比如承重墙是肯定不能打的.
而这些固定好的条条框框就是上层框架给的约束,下层不同的实现就有业主自己决定;所以对于整栋楼来说框架都是固定好的,让业主在有限的范围内自由发挥也方便物业的管理.
以我这个案例的背景为例,首先需要定义出上层框架:
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 自然也可以实现:
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 相关的类都有具体的应用.
最后此篇关于模板方法实践的文章就讲到这里了,如果你想了解更多关于模板方法实践的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我想了解 Ruby 方法 methods() 是如何工作的。 我尝试使用“ruby 方法”在 Google 上搜索,但这不是我需要的。 我也看过 ruby-doc.org,但我没有找到这种方法。
Test 方法 对指定的字符串执行一个正则表达式搜索,并返回一个 Boolean 值指示是否找到匹配的模式。 object.Test(string) 参数 object 必选项。总是一个
Replace 方法 替换在正则表达式查找中找到的文本。 object.Replace(string1, string2) 参数 object 必选项。总是一个 RegExp 对象的名称。
Raise 方法 生成运行时错误 object.Raise(number, source, description, helpfile, helpcontext) 参数 object 应为
Execute 方法 对指定的字符串执行正则表达式搜索。 object.Execute(string) 参数 object 必选项。总是一个 RegExp 对象的名称。 string
Clear 方法 清除 Err 对象的所有属性设置。 object.Clear object 应为 Err 对象的名称。 说明 在错误处理后,使用 Clear 显式地清除 Err 对象。此
CopyFile 方法 将一个或多个文件从某位置复制到另一位置。 object.CopyFile source, destination[, overwrite] 参数 object 必选
Copy 方法 将指定的文件或文件夹从某位置复制到另一位置。 object.Copy destination[, overwrite] 参数 object 必选项。应为 File 或 F
Close 方法 关闭打开的 TextStream 文件。 object.Close object 应为 TextStream 对象的名称。 说明 下面例子举例说明如何使用 Close 方
BuildPath 方法 向现有路径后添加名称。 object.BuildPath(path, name) 参数 object 必选项。应为 FileSystemObject 对象的名称
GetFolder 方法 返回与指定的路径中某文件夹相应的 Folder 对象。 object.GetFolder(folderspec) 参数 object 必选项。应为 FileSy
GetFileName 方法 返回指定路径(不是指定驱动器路径部分)的最后一个文件或文件夹。 object.GetFileName(pathspec) 参数 object 必选项。应为
GetFile 方法 返回与指定路径中某文件相应的 File 对象。 object.GetFile(filespec) 参数 object 必选项。应为 FileSystemObject
GetExtensionName 方法 返回字符串,该字符串包含路径最后一个组成部分的扩展名。 object.GetExtensionName(path) 参数 object 必选项。应
GetDriveName 方法 返回包含指定路径中驱动器名的字符串。 object.GetDriveName(path) 参数 object 必选项。应为 FileSystemObjec
GetDrive 方法 返回与指定的路径中驱动器相对应的 Drive 对象。 object.GetDrive drivespec 参数 object 必选项。应为 FileSystemO
GetBaseName 方法 返回字符串,其中包含文件的基本名 (不带扩展名), 或者提供的路径说明中的文件夹。 object.GetBaseName(path) 参数 object 必
GetAbsolutePathName 方法 从提供的指定路径中返回完整且含义明确的路径。 object.GetAbsolutePathName(pathspec) 参数 object
FolderExists 方法 如果指定的文件夹存在,则返回 True;否则返回 False。 object.FolderExists(folderspec) 参数 object 必选项
FileExists 方法 如果指定的文件存在返回 True;否则返回 False。 object.FileExists(filespec) 参数 object 必选项。应为 FileS
我是一名优秀的程序员,十分优秀!