(點選上方公眾號,可快速關註)
來源:琴水玉 ,
www.cnblogs.com/lovesqcc/p/9568899.html
問題
詳情頁的一些按鈕邏輯,很容易因為產品的策略變更而變化,或因為來了新業務而新增條件判斷,或因為不同業務的差異性而有所不同。如果透過程式碼來實現,通常要寫一串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