问题
详情页的一些按钮逻辑,很容易因为产物的计策改观而变革,或因为来了新业务而新增条件判定,或因为差异业务的差别性而有所差异。假如通过代码来实现,凡是要写一串if-elseif-elseif-else语句,且后续修改扩展较量容易堕落,需要从头宣布,机动性差。 可回收设置化的要领来实现按钮逻辑,昆山软件开发,从而在需要修改的时候只要改观设置即可。按钮逻辑的代码形式一般是:
public Boolean getIsAllowBuyAgain() {
if (ConditionA) {
return BoolA;
}
if (ConditionB) {
return BoolB;
}
if (CondtionC && !CondtionD && (ConditionE not in [v1,v2])) {
return BoolC;
}
return BoolD;
}
本文接头了三种可选方案: 重量级的Groovy剧本方案、轻量级的法则引擎方案、超轻量级的条件匹配表达式方案,重点讲授了条件匹配表达式方案。
这里的代码实现仅作为demo, 实际需要思量结实性及更多因素。 按钮逻辑实现回收了“组合模式”,理会设置回收了“计策模式”和“工场模式”。
利用Groovy缓存剧本
利益:很是机动通用,重量级设置方案
不敷:耗时大概较量多,简朴script剧本第一次执行较量慢, script剧本缓存后执行较量快, 可以思量预热; 巨大的代码不易于设置,简朴逻辑是可以利用Groovy设置的。
package button
import com.alibaba.fastjson.JSON
import org.junit.Test
import shared.conf.GlobalConfig
import shared.script.ScriptExecutor
import spock.lang.Specification
import spock.lang.Unroll
import zzz.study.patterns.composite.button.*
class ButtonConfigTest extends Specification {
ScriptExecutor scriptExecutor = new ScriptExecutor()
GlobalConfig config = new GlobalConfig()
def setup() {
scriptExecutor.globalConfig = config
scriptExecutor.init()
}
@Test
def "testComplexConfigByGroovy"() {
when:
Domain domain = new Domain()
domain.state = 20
domain.orderNo = 'E0001'
domain.orderType = 0
then:
testCond(domain)
}
void testCond(domain) {
Binding binding = new Binding()
binding.setVariable("domain", domain)
def someButtonLogicFromApollo = 'domain.orderType == 10 && domain.state != null && domain.state != 20'
println "domain = " + JSON.toJSONString(domain)
(0..100).each {
long start = System.currentTimeMillis()
println "someButtonLogicFromApollo ? " +
scriptExecutor.exec(someButtonLogicFromApollo, binding)
long end = System.currentTimeMillis()
println "costs: " + (end - start) + " ms"
}
}
}
class Domain {
/** 订单编号 */
String orderNo
/** 订单状态 */
Integer state
/** 订单范例 */
Integer orderType
}
package shared.script;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import groovy.lang.Binding;
import groovy.lang.Script;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import shared.conf.GlobalConfig;
@Component("scriptExecutor")
public class ScriptExecutor {
private static Logger logger = LoggerFactory.getLogger(ScriptExecutor.class);
private LoadingCache<String, GenericObjectPool<Script>> scriptCache;
@Resource
private GlobalConfig globalConfig;
@PostConstruct
public void init() {
scriptCache = CacheBuilder
.newBuilder().build(new CacheLoader<String, GenericObjectPool<Script>>() {
@Override
public GenericObjectPool<Script> load(String script) {
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxTotal(globalConfig.getCacheMaxTotal());
poolConfig.setMaxWaitMillis(globalConfig.getMaxWaitMillis());
return new GenericObjectPool<Script>(new ScriptPoolFactory(script), poolConfig);
}
});
logger.info("success init scripts cache.");
}
public Object exec(String scriptPassed, Binding binding) {
GenericObjectPool<Script> scriptPool = null;
Script script = null;
try {
scriptPool = scriptCache.get(scriptPassed);
script = scriptPool.borrowObject();
script.setBinding(binding);
Object value = script.run();
script.setBinding(null);
return value;
} catch (Exception ex) {
logger.error("exxec script error: " + ex.getMessage(), ex);
return null;
} finally {
if (scriptPool != null && script != null) {
scriptPool.returnObject(script);
}
}
}
}
法则引擎方案
按钮条件逻辑和法则荟萃很是相似,可以思量回收一款轻量级的法则引擎。通过设置平台来打点按钮逻辑法则。