使用结构型设计模式重构for-in循环与变量声明的冗余类型校验代码
解决方案
核心思路是将固定的校验流程与可变的校验规则、错误提示分离,通过组合而非继承的方式复用公共逻辑,具体实现步骤如下:
第一步:封装差异点为独立接口
首先定义函数式接口,用于承载不同场景下的校验规则和错误生成逻辑:
@FunctionalInterface public interface TypeCompatibilityRule { // 校验类型是否兼容,兼容返回null,不兼容返回对应错误提示 String getIncompatibleError(String leftHandSideType, String rightHandSideType); }
同时定义错误上报的公共接口,统一不同Analyzer的错误提交能力:
public interface ErrorReporter { void addError(Token token, String message); }
第二步:抽取公共校验逻辑到独立工具类
把两段代码里重复的空值判断、流程控制逻辑收拢到公共工具类,所有Analyzer都可以复用:
public class TypeCheckHelper { public static void verifyCompatibility(Token token, String leftType, String rightType, TypeCompatibilityRule rule, ErrorReporter reporter) { // 公共空判断逻辑 if (leftType == null || rightType == null) { return; } // 调用传入的自定义校验规则 String errorMsg = rule.getIncompatibleError(leftType, rightType); if (errorMsg != null) { reporter.addError(token, errorMsg); } } }
第三步:改造原有Analyzer类
两个Analyzer分别实现ErrorReporter接口(原有addError方法不用修改,直接声明实现即可),声明各自的校验规则,调用公共工具类完成校验:
LoopAnalyzer改造后代码
// LoopAnalyzer.java public class LoopAnalyzer implements ErrorReporter { // for-in循环专属校验规则 private static final TypeCompatibilityRule FOR_IN_CHECK_RULE = (leftType, rightType) -> { if (!TypeUtils.isArrayType(leftType, rightType)) { return String.format("Types are not compatible, cannot check containment of %s in %s.", leftType, rightType); } return null; }; @Override public void exitForInLoop(ForInLoopContext ctx) { final String rightHandSideType = this.determineExpressionReturnType(ctx.statementExpression()); TypeCheckHelper.verifyCompatibility(ctx.start, ctx.solType().getText(), rightHandSideType, FOR_IN_CHECK_RULE, this); } // 原有addError方法无需修改,天然满足ErrorReporter接口要求 @Override public void addError(Token token, String message) { // 原有错误上报逻辑 } }
ExpressionStatementAnalyzer改造后代码
// ExpressionStatementAnalyzer.java public class ExpressionStatementAnalyzer implements ErrorReporter { // 变量赋值专属校验规则 private static final TypeCompatibilityRule ASSIGNMENT_CHECK_RULE = (leftType, rightType) -> { if (!TypeUtils.areCompatible(leftType, rightType)) { return String.format("Cannot assign a %s value to a %s variable.", rightType, leftType); } return null; }; @Override public void exitVariableDeclarationStatement(VariableDeclarationStatementContext ctx) { final String rightHandSideType = this.determineExpressionReturnType(ctx.statementExpression()); TypeCheckHelper.verifyCompatibility(ctx.start, ctx.solType().getText(), rightHandSideType, ASSIGNMENT_CHECK_RULE, this); } // 原有addError方法无需修改 @Override public void addError(Token token, String message) { // 原有错误上报逻辑 } }
方案优势
- 完全消除了重复的流程代码,后续公共逻辑迭代只需要修改TypeCheckHelper一处
- 没有破坏两个Analyzer的职责边界,避免了不合理的继承关系,符合组合优于继承的设计原则
- 扩展性强,后续新增其他类型校验场景,只需要新增对应TypeCompatibilityRule实现即可,无需修改现有代码
内容的提问来源于stack exchange,提问作者N. Y. Wirawan
相关产品推荐
相关产品推荐

