开源项目中常有以 Context 命名的类,如 Spring 的 ApplicationContext、Dubbo 的 RpcContext、Netty 的 ChannelHandlerContext 等等。这些类的作用在于传递流程中所用到的各种数据,故称上下文。除了用来满足功能外,上下文类其实背后还体现了一种设计模式,这种设计模式可以帮助我们提升系统的可扩展性。
曾遇到很多系统,业务流程中的某一步因业务原因需要增加功能,而这个功能所需的参数是原来所没有的。因此,实现这个功能的方法首先要增加新参数,而这个参数的生成可能是在很多步之前的一个地方。因此,这个新加了参数的方法调用链上直到参数提供者的每一个环节也都需要增加新参数。如此,一个需求很多地方都需要改动,扩展性可想而知。
Context 模式可以很好地解决这个问题。对于业务核心流程中所用到的主要和次要数据,我们都可以放在 Context 中。当再遇到上述需求时,通常只需直接使用 Context 中的数据即可。即便需要新增加数据,改动点会比不使用 Context 要少很多。
AssignContext 有好处有坏处。 好处是穿插整个生命周期,做到极大化的扩展。 坏处是破坏了面向对象的职责单一的原则,因为AssignContext 拥有了很多数据,本应该是各个对象做的事情,都倾斜到AssignContext 去做了。
/**
* 抽象的执行上下文
*/
public abstract class BaseContext {
//附加的参数
protected Map<String, Object> attachParam;
//表对应areaId
protected String areaId;
//数据仓库辅助
protected RepositorySupport repositorySupport;
//主表名
protected String mainTable;
//赋值字段
protected AssignField assignField;
//需要累加的计算区域(子区域)
protected List<DirectCalculateArea> directCalAreaList;
//监听器传入的DATA
protected ThreadLocal<IBOData> iBOData;
//赋值区域数据集合
protected ThreadLocal<List<IBORowData>> assignAreaData;
//当前赋值区域数据
protected ThreadLocal<IBORowData> curAssignAreaData;
//当前计算区域字段
protected ThreadLocal<List<IBOValueObject>> curDirectCalculateField;
//当前计算区域
protected ThreadLocal<DirectCalculateArea> curDirectCalculateArea;
public BaseContext() {
repositorySupport = RepositorySupportFactory.getRepositorySupport();
attachParam = new HashMap<>();
directCalAreaList = new ArrayList<>();
iBOData = new ThreadLocal<>();
assignAreaData = new ThreadLocal<>();
curAssignAreaData = new ThreadLocal<>();
curDirectCalculateField = new ThreadLocal<>();
curDirectCalculateArea = new ThreadLocal<>();
}
//----------------------------------------------本地线程数据的贫血方法--------------------------------------------------
public IBOData getIBOData() {
return this.iBOData.get();
}
public void setIBOData(IBOData value) {
this.iBOData.set(value);
}
public List<IBORowData> getAssignAreaData() {
return this.assignAreaData.get();
}
public void setAssignAreaData(List<IBORowData> list) {
this.assignAreaData.set(list);
}
public IBORowData getCurAssignAreaData() {
return this.curAssignAreaData.get();
}
public void setCurAssignAreaData(IBORowData value) {
this.curAssignAreaData.set(value);
}
public List<IBOValueObject> getCurDirectCalculateField() {
return curDirectCalculateField.get();
}
public void setCurDirectCalculateField(List<IBOValueObject> curDirectCalculateField) {
this.curDirectCalculateField.set(curDirectCalculateField);
}
public DirectCalculateArea getCurDirectCalculateArea() {
return curDirectCalculateArea.get();
}
public void setCurDirectCalculateArea(DirectCalculateArea value) {
this.curDirectCalculateArea.set(value);
}
/**
* 清理本线程数据,防止内存泄漏和线程复用的bug
*/
public void clear() {
this.iBOData.remove();
this.assignAreaData.remove();
this.curAssignAreaData.remove();
this.curDirectCalculateField.remove();
this.curDirectCalculateArea.remove();
}
//------------------------------------------------业务逻辑------------------------------------------------------------
public void assign(IBOData boData) {
try {
//设置本线程的boData
this.setIBOData(boData);
//获取赋值区域
List<IBORowData> list = this.getRepositorySupport().getRowData(boData, this.getAreaId());
//设置本线程的赋值区域数据
this.setAssignAreaData(list);
//赋值区域为空
if (list == null || list.size() == 0) {
return;
}
//遍历赋值区域的数据行
for (int i = 0; i < list.size(); i++) {
IBORowData rowData = list.get(i);
//设置本线程的当前赋值区域数据
this.setCurAssignAreaData(rowData);
//根据当前行,获取计算区域的值
CalculateResult calculateResult = cal();
//计算值为空
if (calculateResult == null) {
continue;
}
calculateResult.setAssignContext(this);
//给赋值字段值赋值
this.getAssignField().assign(rowData, calculateResult);
}
} catch (Exception e) {
throw new RuleAssignException(e.getMessage());
} finally {
//清除本线程变量
this.clear();
}
}
/**
* 抽象的获取计算结果
* @return
*/
protected abstract CalculateResult cal();
//-----------------------------------------------贫血方法------------------------------------------------------------
public AssignField getAssignField() {
return assignField;
}
public BaseContext setAssignField(AssignField assignField) {
this.assignField = assignField;
return this;
}
public BaseContext setAreaId(String areaId) {
this.areaId = areaId;
return this;
}
public BaseContext setMainTable(String mainTable) {
this.mainTable = mainTable;
return this;
}
public String getAreaId() {
return areaId;
}
public RepositorySupport getRepositorySupport() {
return repositorySupport;
}
public String getMainTable() {
return mainTable;
}
public void addAttachParam(String key, Object param) {
this.attachParam.putIfAbsent(key, param);
}
public Object getAttachParam(String key) {
return this.attachParam.get(key);
}
public List<DirectCalculateArea> getDirectCalAreaList() {
return directCalAreaList;
}
public void addDirectCalculateArea(DirectCalculateArea directCalculateArea) {
this.directCalAreaList.add(directCalculateArea);
}
}
/**
* 直接赋值的执行上下文
*/
public class AssignContext extends BaseContext {
//计算子区域策略
protected CalculateFieldStrategy calculateFieldStrategy;
//计算子区域的开放接口
protected CalculateFieldHandler calculateFieldHandler;
public AssignContext() {
super();
}
//-----------------------------------------------业务方法------------------------------------------------------------
/**
* 获取计算区域的数值
* @return
*/
protected CalculateResult cal() {
List<IBOValueObject> iboValueObjects = new ArrayList<>();
List<DirectCalculateArea> directCalAreaList = this.getDirectCalAreaList();
//遍历计算区域
for (int i = 0; i < directCalAreaList.size(); i++) {
DirectCalculateArea directCalculateArea = directCalAreaList.get(i);
//设置本线程的当前计算区域
this.setCurDirectCalculateArea(directCalculateArea);
//当前计算区域的字段值集合
iboValueObjects.addAll(directCalculateArea.getCalculateAreaStrategy().getFieldData(this));
}
//设置本线程的当前计算区域的字段值集合
setCurDirectCalculateField(iboValueObjects);
//计算结果
if (this.getCalculateFieldStrategy() != null) {
return this.getCalculateFieldStrategy().action(this);
} else if (this.getCalculateFieldHandler() != null) {
return this.getCalculateFieldHandler().action(this);
}
return null;
}
//-----------------------------------------------添加计算区域的方法----------------------------------------------------
/**
* 添加计算区域
* @param directCalArea
*/
public void addCalArea(DirectCalculateArea directCalArea) {
getDirectCalAreaList().add(directCalArea);
}
public void addCalAreaById(String areaId, String fieldCode) {
addCalArea(new DirectCalculateArea(areaId, fieldCode));
}
public void addCalAreaById(String areaId, String fieldCode, CalculateAreaStrategy calculateAreaStrategy) {
addCalArea(new DirectCalculateArea(areaId, fieldCode).setCalculateAreaStrategy(calculateAreaStrategy));
}
public void addCalAreaByCode(String mainTable, String areaCode, String fieldCode) {
if (this.mainTable == null) {
this.mainTable = mainTable;
} else {
if (this.mainTable != mainTable) {
throw new RuleAssignException("mainTable is not equal, can't add calarea");
}
}
addCalAreaById(this.getRepositorySupport().getBoAreaIdByMainTableAndAreaCode(mainTable, areaCode), fieldCode);
}
public void addCalAreaByCode(String mainTable, String areaCode, String fieldCode, CalculateAreaStrategy calculateAreaStrategy) {
if (this.mainTable == null) {
this.mainTable = mainTable;
} else {
if (this.mainTable != mainTable) {
throw new RuleAssignException("mainTable is not equal, can't add calarea");
}
}
addCalAreaById(this.getRepositorySupport().getBoAreaIdByMainTableAndAreaCode(mainTable, areaCode), fieldCode, calculateAreaStrategy);
}
public void addCalAreaByCode(String areaCode, String fieldCode) {
if (this.mainTable == null) {
throw new RuleAssignException("mainTable is null, can't add calarea");
}
addCalAreaByCode(this.mainTable, areaCode, fieldCode);
}
public void addCalAreaByCode(String areaCode, String fieldCode, CalculateAreaStrategy calculateAreaStrategy) {
if (this.mainTable == null) {
throw new RuleAssignException("mainTable is null, can't add calarea");
}
addCalAreaByCode(this.mainTable, areaCode, fieldCode, calculateAreaStrategy);
}
//-------------------------------------------------贫血方法-----------------------------------------------------------
public AssignContext setAreaId(String areaId) {
this.areaId = areaId;
return this;
}
public AssignContext setMainTable(String mainTable) {
this.mainTable = mainTable;
return this;
}
public BaseContext setCalculateFieldStrategy(CalculateFieldStrategy calculateFieldStrategy) {
this.calculateFieldStrategy = calculateFieldStrategy;
return this;
}
public BaseContext setCalculateFieldHandler(CalculateFieldHandler calculateFieldHandler) {
this.calculateFieldHandler = calculateFieldHandler;
return this;
}
public CalculateFieldStrategy getCalculateFieldStrategy() {
return calculateFieldStrategy;
}
public CalculateFieldHandler getCalculateFieldHandler() {
return calculateFieldHandler;
}
}
/**
* 表达式赋值的执行上下文
*/
public class ExpressionContext extends BaseContext {
//整个表达式
private String expression;
//=右侧的表达式
private String calAreaString;
//=右侧的占位符表达式
private String calAreaTakePlaceString;
public ExpressionContext(String expression) {
super();
this.expression = expression;
}
//-----------------------------------------------业务方法------------------------------------------------------------
protected CalculateResult cal() {
List<DirectCalculateArea> directCalAreaList = this.getDirectCalAreaList();
List<CalculateResult> calculateResults = new ArrayList<>();
for (int i = 0; i < directCalAreaList.size(); i++) {
List<IBOValueObject> iboValueObjects = new ArrayList<>();
//遍历计算区域
ExpressionCalculateArea directCalculateArea = (ExpressionCalculateArea)directCalAreaList.get(i);
//设置本线程的当前计算区域
this.setCurDirectCalculateArea(directCalculateArea);
//当前计算区域的字段值集合
iboValueObjects.addAll(directCalculateArea.getCalculateAreaStrategy().getFieldData(this));
//设置本线程的当前计算区域的字段值集合
setCurDirectCalculateField(iboValueObjects);
CalculateResult calculateResult = directCalculateArea.getCalculateFieldStrategy().action(this);
if (calculateResult != null) {
calculateResults.add(calculateResult);
} else {
//没有获取到值,放个占位符吧
calculateResults.add(new CalculateResult(null));
}
}
//通过计算器计算结果
return ExpressionCalculator.getInstance().cal(this.calAreaTakePlaceString, calculateResults);
}
//-----------------------------------------------贫血方法------------------------------------------------------------
public String getCalAreaString() {
return calAreaString;
}
public void setCalAreaString(String calAreaString) {
this.calAreaString = calAreaString;
}
public String getCalAreaTakePlaceString() {
return calAreaTakePlaceString;
}
public void setCalAreaTakePlaceString(String calAreaTakePlaceString) {
this.calAreaTakePlaceString = calAreaTakePlaceString;
}
public String getExpression() {
return expression;
}
}
2万+

被折叠的 条评论
为什么被折叠?



