設計模式學習(二十二):解釋器模式
設計模式學習(二十二):解釋器模式
作者:Grey
原文地址:
解釋器模式
解釋器模式是一種行為型模式。
解釋器模式為某個語言定義它的語法(或者叫文法)表示,並定義一個解釋器用來處理這個語法。
一般用於腳本語言解釋器。
示例:如何實現一個自定義介面告警規則功能?
一般來講,監控系統支援開發者自定義告警規則,比如我們可以用下面這樣一個表達式,來表示一個告警規則,它表達的意思是:每分鐘 API 總出錯數超過 100 或者每分鐘 API 總調用數超過 10000 就觸發告警。
api_error_per_minute > 100 || api_count_per_minute > 10000
在監控系統中,告警模組只負責根據統計數據和告警規則,判斷是否觸發告警。至於每分鐘 API 介面出錯數、每分鐘介面調用數等統計數據的計算,是由其他模組來負責的。其他模組將統計數據放到一個 Map 中(數據的格式如下所示),發送給告警模組。接下來,我們只關注告警模組。
Map<String, Long> apiStat = new HashMap<>();
apiStat.put("api_error_per_minute", 103);
apiStat.put("api_count_per_minute", 987);
為了簡化講解和程式碼實現,我們假設自定義的告警規則只包含||、&&、>、<、==
這五個運算符,其中,>、<、==
運算符的優先順序高於||、&&
運算符,&&
運算符優先順序高於||
。在表達式中,任意元素之間需要通過空格來分隔。除此之外,用戶可以自定義要監控的 key,比如前面的 api_error_per_minute、api_count_per_minute。
public class AlertRuleInterpreter {
// key1 > 100 && key2 < 1000 || key3 == 200
public AlertRuleInterpreter(String ruleExpression) {
//TODO:由你來完善
}
//<String, Long> apiStat = new HashMap<>();
//apiStat.put("key1", 103);
//apiStat.put("key2", 987);
public boolean interpret(Map<String, Long> stats) {
//TODO:由你來完善
}
}
public class DemoTest {
public static void main(String[] args) {
String rule = "key1 > 100 && key2 < 30 || key3 < 100 || key4 == 88";
AlertRuleInterpreter interpreter = new AlertRuleInterpreter(rule);
Map<String, Long> stats = new HashMap<>();
stats.put("key1", 101l);
stats.put("key3", 121l);
stats.put("key4", 88l);
boolean alert = interpreter.interpret(stats);
System.out.println(alert);
}
}
實際上,我們可以把自定義的告警規則,看作一種特殊「語言」的語法規則。我們實現一個解釋器,能夠根據規則,針對用戶輸入的數據,判斷是否觸發告警。利用解釋器模式,我們把解析表達式的邏輯拆分到各個小類中,避免大而複雜的大類的出現。
public interface Expression {
boolean interpret(Map<String, Long> stats);
}
public class GreaterExpression implements Expression {
private String key;
private long value;
public GreaterExpression(String strExpression) {
String[] elements = strExpression.trim().split("\\s+");
if (elements.length != 3 || !elements[1].trim().equals(">")) {
throw new RuntimeException("Expression is invalid: " + strExpression);
}
this.key = elements[0].trim();
this.value = Long.parseLong(elements[2].trim());
}
public GreaterExpression(String key, long value) {
this.key = key;
this.value = value;
}
@Override
public boolean interpret(Map<String, Long> stats) {
if (!stats.containsKey(key)) {
return false;
}
long statValue = stats.get(key);
return statValue > value;
}
}
// LessExpression/EqualExpression跟GreaterExpression程式碼類似,這裡就省略了
public class AndExpression implements Expression {
private List<Expression> expressions = new ArrayList<>();
public AndExpression(String strAndExpression) {
String[] strExpressions = strAndExpression.split("&&");
for (String strExpr : strExpressions) {
if (strExpr.contains(">")) {
expressions.add(new GreaterExpression(strExpr));
} else if (strExpr.contains("<")) {
expressions.add(new LessExpression(strExpr));
} else if (strExpr.contains("==")) {
expressions.add(new EqualExpression(strExpr));
} else {
throw new RuntimeException("Expression is invalid: " + strAndExpression);
}
}
}
public AndExpression(List<Expression> expressions) {
this.expressions.addAll(expressions);
}
@Override
public boolean interpret(Map<String, Long> stats) {
for (Expression expr : expressions) {
if (!expr.interpret(stats)) {
return false;
}
}
return true;
}
}
public class OrExpression implements Expression {
private List<Expression> expressions = new ArrayList<>();
public OrExpression(String strOrExpression) {
String[] andExpressions = strOrExpression.split("\\|\\|");
for (String andExpr : andExpressions) {
expressions.add(new AndExpression(andExpr));
}
}
public OrExpression(List<Expression> expressions) {
this.expressions.addAll(expressions);
}
@Override
public boolean interpret(Map<String, Long> stats) {
for (Expression expr : expressions) {
if (expr.interpret(stats)) {
return true;
}
}
return false;
}
}
public class AlertRuleInterpreter {
private Expression expression;
public AlertRuleInterpreter(String ruleExpression) {
this.expression = new OrExpression(ruleExpression);
}
public boolean interpret(Map<String, Long> stats) {
return expression.interpret(stats);
}
}
解釋器模式的應用
- Spring 中的 ExpressionParser