
本文讲解如何使用 Java 8 的 Predicate 接口重构重复的 token 匹配逻辑,将条件判断抽象为可复用的高阶方法,在保持代码清晰的前提下有效遵循 DRY(Don’t Repeat Yourself)原则。
本文讲解如何使用 java 8 的 `predicate` 接口重构重复的 token 匹配逻辑,将条件判断抽象为可复用的高阶方法,在保持代码清晰的前提下有效遵循 dry(don’t repeat yourself)原则。
在实际解析器或词法分析器开发中,常需对当前 token 进行多种条件匹配(如类型精确匹配、类实例检查等)。原始代码中存在明显的逻辑重复:两个 match 方法均执行相同的流程——先校验 hasToken(_currentTokenNum),再获取 getToken(tokenNum),最后依据不同条件判断是否返回该 token。这种重复不仅增加维护成本,更易引发一致性缺陷(例如未来修改边界检查逻辑时遗漏某一处)。
采用函数式抽象是解决此类 DRY 违反的典型方案。核心思路是将变化的条件提取为参数,而将不变的执行流程(空检查 → 获取 token → 条件测试 → 返回)封装为单一实现:
public Token match(Predicate<Token> predicate, int tokenNum) {
if (hasToken(_currentTokenNum)) {
Token t = getToken(tokenNum);
if (predicate.test(t)) {
return t;
}
}
return null;
}在此基础上,原有两个公开方法退化为语义明确的“适配器”:
public Token match(ITokenType expectedToken, int tokenNum) {
return match(t -> expectedToken == t.get_type(), tokenNum);
}
public Token match(Class<?> clazz, int tokenNum) {
return match(t -> clazz.isInstance(t.get_type()), tokenNum);
}✅ 优势:
- 所有 token 获取与空安全逻辑集中于一处,后续扩展新匹配方式(如正则匹配、复合类型判断)只需新增一行适配器调用;
- 避免因手动复制粘贴导致的逻辑偏差(如某处漏掉
hasToken检查); - 符合开闭原则——对扩展开放(新增匹配策略),对修改关闭(核心流程无需改动)。
⚠️ 注意事项:
- 过度抽象可能损害可读性。若仅存在两处调用且逻辑极简(如本例),直接保留重复代码反而更直观;
-
Predicate参数应确保无副作用,且test()方法需具备幂等性; - 若
getToken(tokenNum)开销较大,需确认predicate.test(t)不会引发额外计算(本例中t.get_type()是轻量操作,符合预期)。
总结:DRY 并非机械删除所有重复字符,而是识别并隔离真正需要同步演化的逻辑单元。当重复出现在多个模块、频繁变更或涉及复杂流程时,函数式抽象极具价值;而在局部、稳定、低复杂度场景下,适度冗余反而是更务实的选择。关键在于权衡可维护性与可理解性,而非盲目追求“零重复”。


















