通过结构化设计模式重构冗余代码
Refactor redundant code through structural design pattern
我有以下代码片段,用于检查变量赋值与模拟某些 ANTLR 语法的 for-in 行为的语句表达式之间的类型兼容性。
// LoopAnalyzer.java
@Override
public void exitForInLoop(ForInLoopContext ctx) {
final String rightHandSideType = this.determineExpressionReturnType(ctx.statementExpression());
this.verifyTypeCompatibility(ctx.start, ctx.solType().getText(), rightHandSideType);
}
这段代码会读取并判断determineExpressionReturnType
方法所表示的语句表达式return类型,然后通过比较变量声明类型和右边数组的原始形式来验证类型兼容性-手边。更具体地说,给定 for-in 语句示例的片段:
for (string item in items) {
// do something with the item
}
在那个例子中,item
的类型应该与 items
的原始形式匹配,并且 items
本身应该是一个数组。否则,将引发不兼容错误消息,从而阻止 for-in 语句枚举对象 items
。类似于这种行为的类型兼容性检查的逻辑编码在如下所示的 verifyTypeCompatibility
方法中:
// LoopAnalyzer.java
private void verifyTypeCompatibility(Token token, String leftHandSideType, String rightHandSideType) {
if (rightHandSideType == null || leftHandSideType == null) {
return;
}
if (!TypeUtils.isArrayType(leftHandSideType, rightHandSideType)) {
this.addError(token, String.format("Types are not compatible, cannot check containment of %s in %s.", leftHandSideType, rightHandSideType));
}
}
代码看起来不错,直到我意识到 for-in 语句的类型检查和变量声明的类型检查非常相似,只有很小的差异。与for-in语句不同,变量声明语句只需要语句表达式类型等于分配给变量名的类型。
为简单起见,检查变量声明语句的代码片段如下所示:
// ExpressionStatementAnalyzer.java
@Override
public void exitVariableDeclarationStatement(VariableDeclarationStatementContext ctx) {
final String rightHandSideType = this.determineExpressionReturnType(ctx.statementExpression());
this.verifyTypeCompatibility(ctx.start, ctx.solType().getText(), rightHandSideType);
}
private void verifyTypeCompatibility(Token token, String leftHandSideType, String rightHandSideType) {
if (rightHandSideType == null || leftHandSideType == null) {
return;
}
if (!TypeUtils.areCompatible(leftHandSideType, rightHandSideType)) {
this.addError(token, String.format("Cannot assign a %s value to a %s variable.", rightHandSideType, leftHandSideType));
}
}
有了这些细微的差异,我们如何提取相似的组件以在两个 classes 中重复使用?
目前,我只能想到在 ExpressionStatementAnalyzer class 中保留 determineExpressionReturnType
和 verifyTypeCompatibility
并让 LoopAnalyzer 扩展 class 的结构设计模式它可以覆盖这两种方法的当前可用实现。然而,这种方法不太可能成为解决方案,因为分析循环语句与分析语句表达式无关(即,它们应该不会相互继承)。所以我问了这个问题。
LoopAnalyzer 绝对不是 ExpressionStatementAnalyzer。
您可以添加一个公开 verifyTypeCompatibility 方法的抽象 AbstractAnalyzer:
public abstract class AbstractAnalyzer {
protected void verifyTypeCompatibility(Token token, String leftHandSideType, String rightHandSideType)
{
if (rightHandSideType == null || leftHandSideType == null) {
return;
}
if (!IsTypeCompatible(leftHandSide, rightHandSide) {
this.addError(token, String.format("Cannot assign a %s value to a %s variable.", rightHandSideType, leftHandSideType));
}
}
protected abstract bool IsTypeCompatible(string leftHandSide, string rightHandSide);
}
那么你可以让LoopAnalyzer和ExpressionStatementAnalyzer都扩展AbstractAnalyzer并实现IsTypeCompatible方法。
如果你能把exitVariableDeclarationStatement和exitForInLoopStatement的名字改成exitStatement你也可以把这个方法的实现移到AbstractAnalyzer
我有以下代码片段,用于检查变量赋值与模拟某些 ANTLR 语法的 for-in 行为的语句表达式之间的类型兼容性。
// LoopAnalyzer.java
@Override
public void exitForInLoop(ForInLoopContext ctx) {
final String rightHandSideType = this.determineExpressionReturnType(ctx.statementExpression());
this.verifyTypeCompatibility(ctx.start, ctx.solType().getText(), rightHandSideType);
}
这段代码会读取并判断determineExpressionReturnType
方法所表示的语句表达式return类型,然后通过比较变量声明类型和右边数组的原始形式来验证类型兼容性-手边。更具体地说,给定 for-in 语句示例的片段:
for (string item in items) {
// do something with the item
}
在那个例子中,item
的类型应该与 items
的原始形式匹配,并且 items
本身应该是一个数组。否则,将引发不兼容错误消息,从而阻止 for-in 语句枚举对象 items
。类似于这种行为的类型兼容性检查的逻辑编码在如下所示的 verifyTypeCompatibility
方法中:
// LoopAnalyzer.java
private void verifyTypeCompatibility(Token token, String leftHandSideType, String rightHandSideType) {
if (rightHandSideType == null || leftHandSideType == null) {
return;
}
if (!TypeUtils.isArrayType(leftHandSideType, rightHandSideType)) {
this.addError(token, String.format("Types are not compatible, cannot check containment of %s in %s.", leftHandSideType, rightHandSideType));
}
}
代码看起来不错,直到我意识到 for-in 语句的类型检查和变量声明的类型检查非常相似,只有很小的差异。与for-in语句不同,变量声明语句只需要语句表达式类型等于分配给变量名的类型。
为简单起见,检查变量声明语句的代码片段如下所示:
// ExpressionStatementAnalyzer.java
@Override
public void exitVariableDeclarationStatement(VariableDeclarationStatementContext ctx) {
final String rightHandSideType = this.determineExpressionReturnType(ctx.statementExpression());
this.verifyTypeCompatibility(ctx.start, ctx.solType().getText(), rightHandSideType);
}
private void verifyTypeCompatibility(Token token, String leftHandSideType, String rightHandSideType) {
if (rightHandSideType == null || leftHandSideType == null) {
return;
}
if (!TypeUtils.areCompatible(leftHandSideType, rightHandSideType)) {
this.addError(token, String.format("Cannot assign a %s value to a %s variable.", rightHandSideType, leftHandSideType));
}
}
有了这些细微的差异,我们如何提取相似的组件以在两个 classes 中重复使用?
目前,我只能想到在 ExpressionStatementAnalyzer class 中保留 determineExpressionReturnType
和 verifyTypeCompatibility
并让 LoopAnalyzer 扩展 class 的结构设计模式它可以覆盖这两种方法的当前可用实现。然而,这种方法不太可能成为解决方案,因为分析循环语句与分析语句表达式无关(即,它们应该不会相互继承)。所以我问了这个问题。
LoopAnalyzer 绝对不是 ExpressionStatementAnalyzer。 您可以添加一个公开 verifyTypeCompatibility 方法的抽象 AbstractAnalyzer:
public abstract class AbstractAnalyzer {
protected void verifyTypeCompatibility(Token token, String leftHandSideType, String rightHandSideType)
{
if (rightHandSideType == null || leftHandSideType == null) {
return;
}
if (!IsTypeCompatible(leftHandSide, rightHandSide) {
this.addError(token, String.format("Cannot assign a %s value to a %s variable.", rightHandSideType, leftHandSideType));
}
}
protected abstract bool IsTypeCompatible(string leftHandSide, string rightHandSide);
}
那么你可以让LoopAnalyzer和ExpressionStatementAnalyzer都扩展AbstractAnalyzer并实现IsTypeCompatible方法。
如果你能把exitVariableDeclarationStatement和exitForInLoopStatement的名字改成exitStatement你也可以把这个方法的实现移到AbstractAnalyzer