写在前面的话:
其实这个模式的名字是我瞎想的,因为它的功能与名字的概念很相似,稍后详细介绍一下这种模式是怎样的设计思路。
一、设计思路
农民伯伯获取农产品的方式有很多种,有些人先选水资源丰富的地方,先犁田,再灌水,种水稻,再杀虫,再收割,晾晒,脱壳。有些人选择种大豆,在田埂或者近水源的地方,耕地,种大豆,浇水,杀虫,收大豆。(只是举例子,肯定流程有不对的地方,就不要纠结了)。那么这些工作都有同样一个特点:在一条流程中间分布着很多步骤,每一个步骤承上启下,各个流程之间互不干扰,但是流程的步骤可能有相似的地方,通常我们写代码也是这样,一个流程一个流程从开始到结束的写,流程中间有相似的流程步骤,但是由于参数或者其他业务原因,没有复用到这些步骤,很多代码虽然不是重复代码,但是实现的作用是一样的,导致开发效率低下,那么粒子-流模式的设计思路就可以解决这种问题。
粒子-流模式,流代表流程,粒子代表流程中的步骤,流由粒子组装而成,不同的粒子可以组装成不同的流,就跟积*木一样,千变万化,最重要的是,粒子可以绝对复用,当然,还是得靠代码写得好。
二、粒子-流模式的基类
这个模式的基类有很多种,现在以能实现这种模式的最简单的方式来说一下:
1.参数基类:
为了保证粒子在所有流中都通用,所有的出入参数都将继承一个参数基类,另外,流中可能需要记录一些其他信息,因为还需要一个上下文基类。
2.粒子基类:
每一个粒子类都将继承粒子基类,那么所有流在加载粒子类时,就可以达到通用的目的。
3.流基类:
流基类用以加载粒子类,我们使用流执行器类执行粒子类,规定了一些规范,以便流执行类更好的执行。
4.流执行器:
根据流类提供的规范执行粒子类。
以上就是实现最简单的粒子-流模式的四个基类了。
三、代码实现
我们先定义一个最基本的类:BaseDataType,它提供三个方法:是否包含属性,获取属性,设置属性,这样子类可以在不强转的情况下可以直接获取和设置属性值。所有基类都可以继承这个类,代码实现:
package com.yhd.shaoyu.onlineconfig.vo;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @Description:
* @Author: shaoyu1
* @Date Create in 2018/8/2 15:20
*/
public class BaseDataType implements Serializable {
/**
* 是否包含属性
* @param filedname
* @return
*/
public boolean containsProperty(String filedname){
try{
return this.getClass().getDeclaredField(filedname)!=null;
}catch (NoSuchFieldException e){
e.printStackTrace();
return false;
}
}
/**
* 获取属性(有默认值) 当没有这种属性时,返回默认值
* @param filedName
* @param def
* @return
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
public Object get(String filedName,Object def) throws InvocationTargetException, IllegalAccessException {
if(containsProperty(filedName)){
return get(filedName)==null?def:get(filedName);
}else {
return def;
}
}
/**
* 获取属性
* @param filedName
* @return
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
public Object get(String filedName) throws InvocationTargetException, IllegalAccessException {
if(!containsProperty(filedName)){
return null;
}else{
try{
return getPropertyGetMethod(this.getClass(),filedName).invoke(this);
}catch (NullPointerException e){
throw new RuntimeException("属性"+filedName+"没有对应的get方法");
}
}
}
/**
* 设置属性
* @param filedName
* @param property
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
public void set(String filedName,Object property)throws InvocationTargetException, IllegalAccessException {
if(!containsProperty(filedName)){
throw new RuntimeException("没有对应的属性:"+filedName);
}else {
try{
getPropertySetMethod(this.getClass(),filedName).invoke(this,property);
}catch (NullPointerException e){
throw new RuntimeException("属性"+filedName+"没有对应的set方法");
}
}
}
private Method getPropertyGetMethod(Class<?> type, String filedName) {
Method[] methods = type.getMethods();
for (Method method : methods) {
if (method.getParameterTypes().length != 0)
continue;
if (method.getName().equalsIgnoreCase("get" + filedName)) {
return method;
}
if ((Boolean.class.isAssignableFrom(method.getReturnType()) || "boolean".equals(method.getReturnType().getName())) && method.getName().equalsIgnoreCase("is" + filedName)) {
return method;
}
}
return null;
}
private Method getPropertySetMethod(Class<?> type, String filedName) {
Method[] methods = type.getMethods();
for (Method method : methods) {
if (method.getParameterTypes().length != 1)
continue;
if (method.getName().equalsIgnoreCase("set" + filedName)) {
return method;
}
if ((Boolean.class.isAssignableFrom(method.getParameterTypes()[0]) || "boolean".equals(method.getParameterTypes()[0].getName())) && method.getName().equalsIgnoreCase("set" + filedName.replace("is", ""))) {
return method;
}
}
return null;
}
}
然后我们再定义一下request和response以及context基类:
package com.yhd.shaoyu.onlineconfig.vo;
import com.alibaba.fastjson.JSON;
/**
* @Description:
* @Author: shaoyu1
* @Date Create in 2018/8/2 15:51
*/
public class BaseRequest extends BaseDataType {
@Override
public String toString() {
return JSON.toJSONString(this);
}
}
package com.yhd.shaoyu.onlineconfig.vo;
/**
* @Description:
* @Author: shaoyu1
* @Date Create in 2018/8/2 16:06
*/
public class BaseResponse extends BaseDataType {
public String code;
public String msg;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
package com.yhd.shaoyu.onlineconfig.vo;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @Description:
* @Author: shaoyu1
* @Date Create in 2018/8/2 15:51
*/
public class BaseFlowContext extends BaseDataType {
/**流程中断控制器*/
private boolean skipException;
/**上下文记录信息*/
private Map<String,Object> attributes = new HashMap<>();
/**流程走向*/
private StringBuilder flow = new StringBuilder();
public boolean isSkipException() {
return skipException;
}
public void setSkipException(boolean skipException) {
this.skipException = skipException;
}
/**获取上下文信息*/
public Object getAttribute(String key){
return attributes.get(key);
}
/**设置上下文信息*/
public void setAttribute(String key,Object attr){
if(attr==null){
attributes.remove(key);
}else{
attributes.put(key,attr);
}
}
public String getFlow(){
return this.flow.toString();
}
/**记录流程走向*/
public void putFlow(String className){
if(flow.length()==0){
this.flow.append(className);
}else{
this.flow.append("=>"+className);
}
}
}
然后就是流基类,流基类中有执行器类属性,还有三个abstract方法需要子类实现,另外还有doFlow方法是专门给业务类调用的,当然我这里写的很简单,只是将粒子分成了三个独立的小模块来先后执行,这里还可以写的更好,就不再赘述了:
package com.yhd.shaoyu.onlineconfig.flow;
import com.yhd.shaoyu.onlineconfig.flowExecutor.FlowExecutor;
import com.yhd.shaoyu.onlineconfig.vo.*;
import java.util.ArrayList;
/**
* @Description:
* @Author: shaoyu1
* @Date Create in 2018/8/2 15:49
*/
public abstract class ServiceFlow<requestType extends BaseRequest,responseType extends BaseResponse> {
FlowExecutor executor = FlowExecutor.getExecutor();
public abstract ArrayList<BaseGranule> getPreGranule();
public abstract ArrayList<BaseGranule> getPostGranule();
public abstract ArrayList<BaseGranule> getAfterGranule();
public responseType doFlow(requestType request, responseType response, BaseFlowContext context){
try{
executor.executeGraules("前置粒子处理",getPreGranule(),request,response,context);
executor.executeGraules("主要流程粒子处理",getPostGranule(),request,response,context);
executor.executeGraules("后置粒子处理",getAfterGranule(),request,response,context);
}catch (Exception e){
e.printStackTrace();
return null;
}
return response;
}
}
粒子基类,粒子基类有一个属性step,是用于在流的一个模块中确认执行顺序的,还有两个方法,execute方式是真正做业务逻辑的地方,在子类中必须实现这个方法来写逻辑代码,而doFlow方法则是交给执行器类调用的:
package com.yhd.shaoyu.onlineconfig.vo;
/**
* @Description:
* @Author: shaoyu1
* @Date Create in 2018/8/2 16:02
*/
public abstract class BaseGranule {
private int step;
public abstract void execute(BaseRequest request,BaseResponse response,BaseFlowContext context)throws Exception;
public void doFlow(BaseRequest request,BaseResponse response,BaseFlowContext context) throws Exception {
context.putFlow(this.getClass().getSimpleName());
try{
this.execute(request,response,context);
}catch (Exception e){
context.putFlow(this.getClass().getSimpleName()+"出错:"+e.getMessage());
e.printStackTrace();
throw e;
}
}
public int getStep() {
return step;
}
public void setStep(int step) {
this.step = step;
}
}
类执行器:
package com.yhd.shaoyu.onlineconfig.flowExecutor;
import com.yhd.shaoyu.onlineconfig.vo.BaseFlowContext;
import com.yhd.shaoyu.onlineconfig.vo.BaseGranule;
import com.yhd.shaoyu.onlineconfig.vo.BaseRequest;
import com.yhd.shaoyu.onlineconfig.vo.BaseResponse;
import org.springframework.util.CollectionUtils;
import java.util.Arrays;
import java.util.List;
/**
* @Description:
* @Author: shaoyu1
* @Date Create in 2018/8/2 16:10
*/
public class FlowExecutor<requestType extends BaseRequest,responseType extends BaseResponse> {
private static FlowExecutor executor = new FlowExecutor();
private FlowExecutor(){ super();}
public static FlowExecutor getExecutor() {
return executor;
}
public void executeGraules(String step, List<BaseGranule> granules, requestType request, responseType response, BaseFlowContext context){
if(CollectionUtils.isEmpty(granules)){
context.putFlow(step+"中,没有有效粒子");
}else{
BaseGranule[]baseGranules = granules.toArray(new BaseGranule[0]);
Arrays.sort(baseGranules, (o1, o2) -> o1.getStep()-o2.getStep());
for (BaseGranule granule:baseGranules) {
if(context.isSkipException()){
context.putFlow("上下文终止流程继续进行..");
break;
}
try{
granule.doFlow(request,response,context);
}catch (Exception e){
e.printStackTrace();
}
}
}
response.setMsg(context.getFlow());
}
}
好了,基类就差不多写完了,下面说一个实例,就不贴全部代码了,直接贴粒子类:
粒子1,从request中获取用户名,查询用户信息放到response里去:
@Override public void execute(BaseRequest request, BaseResponse response, BaseFlowContext context) throws Exception { response.set("user",BeanUtil.getBean(ConfigUserRepository.class).findConfigUserByUserName(request.get("userName","").toString())); //context.setSkipException(true); }
粒子2,将response中的用户信息的用户名改成:
@Override public void execute(BaseRequest request, BaseResponse response, BaseFlowContext context) throws Exception { TestResponse testResponse = (TestResponse) response; testResponse.getUser().setUserName("I had changed the username"); }
测试:
TestFlow flow = new TestFlow(); TestRequest request = new TestRequest(); request.setUserName("shaoyu"); TestResponse response = new TestResponse(); BaseFlowContext context = new BaseFlowContext(); flow.doFlow(request,response,context); System.out.println(response.getUser().getUserName()); System.out.println(response.getMsg());
结果:
I had changed the username
前置粒子处理中,没有有效粒子=>TestGranule=>TestGranule1=>后置粒子处理中,没有有效粒子
将粒子1中的注释代码放开再执行一遍:
shaoyu
前置粒子处理中,没有有效粒子=>TestGranule=>上下文终止流程继续进行..=>后置粒子处理中,没有有效粒子