­

設計模式學習(二十二):解釋器模式

設計模式學習(二十二):解釋器模式

作者:Grey

原文地址:

部落格園:設計模式學習(二十二):解釋器模式

CSDN:設計模式學習(二十二):解釋器模式

解釋器模式

解釋器模式是一種行為型模式。

解釋器模式為某個語言定義它的語法(或者叫文法)表示,並定義一個解釋器用來處理這個語法。

一般用於腳本語言解釋器。

示例:如何實現一個自定義介面告警規則功能?

一般來講,監控系統支援開發者自定義告警規則,比如我們可以用下面這樣一個表達式,來表示一個告警規則,它表達的意思是:每分鐘 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);
  }
} 

解釋器模式的應用

UML 和 程式碼

UML 圖

程式碼

更多

設計模式學習專欄

參考資料