读者可以在eclipse中导入附件的项目,执行main.java体验"步骤执行容器"的效果(温馨提示,stepframework依赖了dom4j,在附件中的dependence目录含有该lib)。
问题背景与实现简述
通常情况下,一项任务可以分为多个步骤,每个步骤之下又能分为几个子步骤。最简单的实现方法就是:使用一个主类调用几个步骤方法去完成任务;每个步骤方法执行的时候,能够读取的参数都是同一配置文件下的所有参数。然而,到了后期,任务的需求变得越来越灵活和复杂,前面实现方法就存在以下缺点了:
1 不能做到某些步骤的配置式替换、去掉或者添加;
2 当步骤越来越多的时候,参数命名容易冲突,通常需要添加足够的前缀。况且一个步骤能到其他所有的步骤的参数,也不太合理。
3 参数在步骤间传递的是个设计难题。通常来说,在多个方法或者类之间传递某个值,要么需要使用一个static变量、threadlocal之类的全局变量来维护,要么在方法中传递参数。前者会一定程度上增加维护的难度(需要清楚某个参数何时放进去,何时拿出来,需要知道全局变量里面究竟会有哪些参数),而后者会让代码看起来好臃肿(参数太多了)而且耦合性较强。
4 当步骤的数量庞大起来的时候,让人很难理清其中的执行的顺序和逻辑。
例如做一个增量补丁制作工具,刚开始按照 ”SVN-DIFF-->SVN下载-->全量编译SRC-->增量编译SRC-->复制文件-->整合sql执行脚本文件“的思路做了一个简单的工具。后面发现,有时候需要SVN下载后停顿一下,以便手工删除某些文件;整合sql执行脚本文件针对不同的项目需要替换;后面有人提议希望能够直接只指定某几个步骤,而不是每次都从头到尾执行,太浪费时间了,例如只需要执行整合sql脚本等等。这时候,原来简单的一个主类调用多个步骤方法的程序很难使用。
重构:采用配置文件、责任链模式、聚合模式解决以上问题
1 使用step.xml配置文件描述所有的步骤组件、步骤参数和要执行的步骤顺序。
通过配置该文件,可以灵活地替换、去掉或者添加组件。同时,该文件,能够非常清晰地描述整个任务的执行的过程。
<main>add,multiply,divideAndSubtractionStepSuite</main> <all-step> <step name="add" class="test.Add" params-ref="add"/> <step name="multiply" class="test.Multiply" params-ref="multiply"/> <step name="divideAndSubtractionStepSuite" class="test.TryStepSuit" params-ref="divideAndSubtractionStepSuite" /> </all-step>
看了这个文件,即使我不说明,都大概能猜到该程序要依次运行add,multiply,divideAndSubtractionStepSuite这三个步骤,步骤对应的类和参数在<step/>中得到进一步的描述。
细节1
divideAndSubtractionStepSuite
这个步骤其实集合了除法步骤和减法步骤,融合为一步骤(具体参见附件或实例代码)。这个特性是为了满足可读性的要求。例如一个增量编译,包含了下载增量文件,编译,COPY文件三个子步骤,融合为一个名为compileDiff的步骤,显然好读,更易理解。
细节2
配置文件没有考虑StepSuite的配置项,只有step配置项。
stepframwork将包含多个step和stepSuite组件的集合称为stepSuite。目前对StepSuite下的各个组件共用一个参数集合,因为考虑到一个StepSuite,大多数情况下是不应该存在重复参数的,而且,我认为一个StepSuite作为一个namespace已经足够了,细化到Step似乎有些多余。万一如果一个StepSuite下真存在重复组件和重复参数名,实际上很可能应该将一个StepSuite分拆为两个。另一方面,考虑到如果要使每个StepSuite组件下的所有Step单独使用自己的参数集合,配置将变得复杂,后面的人维护起来也麻烦。stepframwork并不是不能实现每个Step对应一个参数集合,而是为了维护和易学,stepframework做了一个“不完美”的选择, 因此配置文件中没有类似
<stepSuite name="divideAndSub">
<step step-ref="divide"/>
<step step-ref="sub"/>
</stepSuite>
的配置。但是可以通过写一个TestSuite的子类实现同等效果,也很简单。
例如:
package test; import stepframework.StepSuite; public class TryStepSuit extends StepSuite{ public TryStepSuit(){ this.addStep(new Divide()).addStep(new Sub()); } }
2 每个步骤组件关联一个参数集合,当参数集合中查找不到想要的参数,则使用责任链在前面步骤组件关联的参数集合中查找参数。
这样设计是因为通常情况下,后面执行的步骤需要前面步骤的某些参数,而责任链能非常优雅的实现这一点。责任链还提供其他两个好处:
a)免去了重用前面的某个步骤组件时需要在参数名前面添加前缀的问题,因为即使重名,责任链保证首先会读到该组件实例关联的参数。
b)利用责任链,可以把前一步骤的执行结果,传递到下一个步骤。
此外,每个步骤组件关系的参数集合,都是由stepframework自动注入到具体的步骤组件对象中,无需步骤组件开发人员处理。
题外话:在现在使用责任链来实现参数的动态查找之前,我是这样设计的,一个步骤组件关联一个参数集合,该集合包含了该步骤所需的所有参数,但是这些参数与其他步骤的参数有不少重复。对于一个有代码洁癖的程序原来说,这是不能容忍的事情,苦思一番之后,灵感乍现,javascript原型链不就是解决我这个问题的最佳实践吗?于是,责任链的实现诞生了。
<all-params> <params name="add"> <param name="left" value="10"/> <param name="right" value="20"/> <param name="result" value=""/> </params> <params name="multiply"> <param name="multi_num" value="3"/> </params> <params name="divideAndSubtractionStepSuite"> <param name="div_num" value="2"/> <param name="sub_num" value="3"/> </params> </all-params>
package stepframework; import java.util.Map; public class Params { private Map<String,String> map; private Params parent; public Params(Map<String, String> map, Params parent) { super(); this.map = map; this.parent = parent; } public void put(String paramName, String paramValue){ map.put(paramName, paramValue); } //责任链查找 public String get(String paramName){ String paramValue = map.get(paramName); if(null != paramValue){ return paramValue; }else if(null != parent){ return parent.get(paramName); }else{ return ""; } } }
3 对于步骤集合(StepSuite)和单一步骤(Step),使用了聚合模式,统一作为一个IStep的实现,容器通过该方式,统一处理所有的步骤,而无视所谓步骤与子步骤,程序变得更简单。
package stepframework; import java.util.ArrayList; import java.util.List; public class StepSuite implements IStep{ private Params params; private List<IStep> steps = new ArrayList<IStep>(); public StepSuite(){ } public StepSuite addStep(IStep step){ steps.add(step); return this; } //聚合模式的关键,集合对象,依次执行每个子对象 @Override public Result run() { for(IStep step : steps){ step.setParams(params); Result rs = step.run(); if(!rs.isSuccess()){ return rs; } } return Result.SUCCESS; } public void setParams(Params params){ this.params = params; } public void putParam(String paramName, String paramValue){ params.put(paramName, paramValue); } public String getParam(String paramName){ return params.get(paramName); } }
StepFramework使用示例
stepframework使用非常简单,只需两步:1. 扩展基类创建步骤组件,2. step.xml中描述如何运行这些组件
下面演示了加减乘除4个步骤的依次执行,每个步骤执行的结果,都能传递到后一步骤。
1. 编写步骤类,继承Step,或者StepSuite(包含多个Step或者StepSuite)。
下面定义了加减乘除4个Step,和1个综合了“除和减”两个Step的集合StepSuite。
对于单一步骤Step,需要继承Step类,重写run()方法,编写步骤的具体逻辑。在run方法中使用getParam查找参数,使用putParam设置传递到后面步骤的结果参数。如果执行失败,创建一个Result对象,填充错误信息 ,并且stepframework会马上体制,如果执行成功,直接返回Result.SUCCESS即可。
对于Step或StepSuite的步骤集合StepSuite,需要继承StepSuite类,编写构造函数,调用add()方法添加所需的Step或StepSuite。这些Step和StepSuite都共同关联同一个参数结合params。
package test; import stepframework.Result; import stepframework.Step; public class Add extends Step{ @Override public Result run() { String left = this.getParam("left"); String right = this.getParam("right"); Integer i = Integer.valueOf(left) + Integer.valueOf(right); String result = String.valueOf(i); this.putParam("result",result ); System.out.println(left+"+"+right+"="+result); return Result.SUCCESS; } }
package test; import stepframework.Result; import stepframework.Step; public class Multiply extends Step{ @Override public Result run() { String result = this.getParam("result"); String multi_num = this.getParam("multi_num"); String orginResult = result; result = String.valueOf(Integer.valueOf(result)*Integer.valueOf(multi_num)); System.out.println(orginResult+"*"+multi_num+"="+result); this.putParam("result", result); return Result.SUCCESS; } }
package test; import stepframework.Result; import stepframework.Step; public class Divide extends Step{ @Override public Result run() { String result = this.getParam("result"); String divNum = this.getParam("div_num"); String orginResult = result; result = String.valueOf(Integer.valueOf(result)/Integer.valueOf(divNum)); System.out.println(orginResult+"/"+divNum+"="+result); this.putParam("result", result); return Result.SUCCESS; } }
package test; import stepframework.Result; import stepframework.Step; public class Sub extends Step{ @Override public Result run() { String result = this.getParam("result"); String sub_num = this.getParam("sub_num"); String orginResult = result; result = String.valueOf(Integer.valueOf(result) - Integer.valueOf(sub_num)); System.out.println(orginResult+"-"+sub_num+"="+result); this.putParam("result", result); return Result.SUCCESS; } }
package test; import stepframework.StepSuite; public class TryStepSuit extends StepSuite{ public TryStepSuit(){ this.addStep(new Divide()).addStep(new Sub()); } }
2. 配置step.xml,让stepframework自动按顺序执行各个步骤。
a)在step.xml中的<step/>中,添加上步骤名,步骤实现类和引用的参数集合名。
b)在step.xml中的<params/>中,配置每个步骤所需要的参数。
c)在step.xml中main中,配置要执行的步骤,stepframework会按照先后顺序执行。
d)step.xml放在classpath下
<step-root> <main>add,multiply,divideAndSubtractionStepSuite</main> <all-step> <step name="add" class="test.Add" params-ref="add"/> <step name="multiply" class="test.Multiply" params-ref="multiply"/> <step name="divideAndSubtractionStepSuite" class="test.TryStepSuit" params-ref="divideAndSubtractionStepSuite" /> </all-step> <all-params> <params name="add"> <param name="left" value="10"/> <param name="right" value="20"/> <param name="result" value=""/> </params> <params name="multiply"> <param name="multi_num" value="3"/> </params> <params name="divideAndSubtractionStepSuite"> <param name="div_num" value="2"/> <param name="sub_num" value="3"/> </params> </all-params> </step-root>
3 调用StepRunner类执行程序
public class Main { public static void main(String[] args) throws URISyntaxException, DocumentException, ClassNotFoundException, InstantiationException, IllegalAccessException{ StepRunner.run(); } }
相关推荐
步骤 2:运行 CentOS 容器 交互式会话 后台运行容器 步骤 3:管理容器 查看正在运行的容器 停止容器 重新启动容器 连接到已启动的容器 步骤 4:使用 Docker Compose 创建 docker-compose.yml 文件 启动服务 停止服务...
标题"Kettle实现步骤循环执行"所指的就是在Kettle的工作流设计中,如何设置和配置步骤以便于它们能够按预定条件或次数循环执行。这在处理大量或者需要动态迭代的数据时非常有用。 首先,Kettle的工作流是由一系列的...
本篇将深入探讨Servlet的基础知识及其运行环境——Servlet容器模型。 首先,让我们了解Servlet的基础。Servlet是一个Java类,遵循javax.servlet.Servlet接口,它允许开发者扩展服务器的功能。当用户向Web服务器发送...
### Qt5.9.1可执行程序转换为安装文件的步骤详解 #### 一、简述 本篇文章将详细介绍如何将Qt5.9.1开发的可执行程序转化为可直接安装使用的`.exe`安装文件。这不仅适用于Qt5.9.1版本,也适用于其他版本的Qt框架开发...
《压力容器设计步骤详解》 压力容器是一种在工业生产中广泛应用的重要设备,它主要用于存储、运输或处理具有高压、高温的气体或液体。其设计过程严谨且复杂,涉及到材料选择、强度计算、工艺流程等多个方面。以下是...
小黄鸭容器直装是一款专为用户打造的轻量级应用程序容器化工具,它使得用户无需复杂的配置步骤,即可直接安装和运行各种应用程序。这款软件的主要功能在于提供一个虚拟化的环境,让应用能在独立的、隔离的环境中运行...
1. **EasyWebSvr.exe**:这很可能是一个可执行文件,代表了我们所讨论的简单迅速的Web容器。它可能是Web服务器的二进制程序,用户可以通过运行这个文件来启动和管理Web服务。这种类型的Web服务器往往设计简洁,易于...
容器云搭建3master操作步骤
下面将详细介绍 Kettle 部署和定时作业执行的步骤。 一、java 安装和配置 在部署 Kettle 之前,需要安装和配置 java 环境。首先,需要查看系统是否已经安装了 JDK,可以使用命令“java -version”来检查。如果没有...
控制部分主要由伺服放大器、手操器和电动执行机构组成,它们按照PLC发出的指令,对进料阀和排空阀等执行元件进行操作,以实现对压力容器内参数的精确控制。 上位机程序方面,本系统选择了Realinfo组态软件,它能够...
容器 runtime 负责容器的执行环境,提供了容器的网络、存储和安全性等功能。容器网络是容器服务的基本组件,提供了容器之间的网络通信功能。存储管理是容器服务的重要组件,负责容器的存储管理和数据存储。监控与...
9. **安全措施**:由于压力容器工作时可能存在高压、高温风险,因此施工过程中必须严格执行安全规程,配备必要的防护装备,设立安全警示,防止事故发生。 10. **后期维护**:安装完毕后,压力容器需定期进行检查和...
文件“2007ZDH2007LW11000093.doc”可能进一步提供了关于操作规程、标准规范和实际案例的具体信息,对于理解和执行液压油液取样容器的净化方法及其鉴定控制具有重要的指导价值。在实际操作过程中,我们应认真阅读...
本文档涵盖了容器云平台的配置步骤,从平台设置到节点设置,finally 到Pods 或容器设置和镜像仓库配置。 平台设置是容器云平台的基础设置,包括功能菜单配置、Kube 配置、Etcd 配置、Docker Daemon 设置和Beats ...
5. **焊接工艺**:焊接是压力容器制造中的关键环节,涉及坡口制备、焊接方法(如手工电弧焊、氩弧焊等)、焊接参数选择、焊接顺序等,必须制定并执行焊接工艺规程。 6. **无损检测**:包括超声波检测、射线检测、...
5. **作业设计**:如果需要定期执行此转换,可以将这些步骤封装到一个转换中,然后在Kettle的作业中调度该转换的执行。作业可以设置定时器,使其按照预定的时间间隔自动运行。 在实际操作中,可能还需要考虑错误...
处罚工作执行步骤及要领
【压力容器制造工艺纪律】是制造压力容器过程中必须遵循的...以上各环节的严格执行是保证压力容器安全、可靠的关键。每一步骤都有严格的工艺纪律,以确保最终制造出的压力容器能满足设计要求,具备良好的性能和安全性。
Cube-IQ 使用步骤详解 ...Cube-IQ 的使用步骤是先创建基本数据,然后建立装载记录,添加装载容器和包装箱,最后优化生成装载方案。用户需要根据实际情况设置优化参数和计量单位,提高装载效率和空间利用率。
金蝶容器重新部署war包的过程涉及多个关键步骤和技术细节,下面将对这一过程进行详细的说明。 首先,需要了解什么是war包。war(Web Application ...通过仔细规划和执行上述步骤,可以确保应用的平稳升级和运行。