大家好,我是征哥,今天分享一种设计模式,解释器模式。
先来看一个需求:
在告警系统中,有很多规则的配置,如果配置的规则被触发,监控系统就通过短信、微信、邮件等方式发送告警给开发者。比如,每分钟 API 总出错数超过 100 或者每分钟 API 总调用数超过 10000 就触发告警。配置的规则如下:
api_error_per_minute > 9 || api_count_per_minute > 10000
在监控系统中,告警模块只判断是否触发告警。至于每分钟 API 接口出错数、每分钟接口调用数等统计数据的计算,是由其他模块来负责的。其他模块将统计数据放到一个 dict 中,数据的格式如下所示:
apiStat = {}
apiStat["api_error_per_minute"] = 10
apiStat["api_count_per_minute"] = 987
接下来,编写程序,输入是一个字典,代表统计数据 apiStat,和一个字符串,代表告警规则 "api_error_per_minute > 9 || api_count_per_minute > 10000",输出:True 或 False,True 表述满足告警规则,False 表示不满足。
为了简化代码实现,我们假设自定义的告警规则只包含“||、&&、>、<、==”这五个运算符,其中,“>、<、==”运算符的优先级高于“||、&&”运算符,“&&”运算符优先级高于“||”。在表达式中,任意元素之间需要通过空格来分隔。
除此之外,用户可以自定义要监控的 key,比如前面的 api_error_per_minute、api_count_per_minute。
那么如何写代码实现呢?
更具体一点,请将以下 pass 语句替换成可以执行的代码,其中 rule 字符串是可以自由变化的:
class AlertRuleInterpreter:
def __init__(self, ruleExpression: str):
pass
def interpret(self, stats: dict) -> bool:
pass
if __name__ == "__main__":
rule = "key1 > 100 && key2 < 30 || key3 < 100 || key4 == 88"
interpreter = AlertRuleInterpreter(rule)
stats = {}
stats["key1"] = 101
stats["key3"] = 121
stats["key4"] = 88
alert = interpreter.interpret(stats)
print(alert)
你可以先暂停,思考下怎么写,然后和我这里对比一下:
基础版本
思路:先把字符串按照 || 分割成子串,每一个字串内部的逻辑关系就是 &&,再将字串按照 && 分割成子子串,每一个子子串的内部逻辑关系就三种:>、<、==
,这是不是很像一个树?