分层访问者模式和状态管理

Hierarchical Visitor Pattern and State Management

我正在尝试找到一种使用分层访问者模式和 ANTLR 自动生成的基本访问者来管理状态的好方法 class。 虽然下面的示例是我编造的一些愚蠢的东西,但我相信它有助于理解我想要解决的概念。

举个例子,假设我们有一个 class:

public class JavaClassVisitor extends JavaBaseVisitor<List<String>> {

    private Map<String, String> dict = new HashMap<>();
    dict.put("public", "I FOUND A PUBLIC SPECIFIER!");
    dict.put("private", "I FOUND A PRIVATE SPECIFIER")

    private List<String> descriptions = new ArrayList<>();

    @Override
    public List<String> visitParseContext(ParseContext ctx){
         visitChildren(ctx);
         return descriptions;
    }

    @Override
    public List<String> visitClassDeclaration(ClassDeclarationContext ctx){
        IdentifierContext idCtx = ctx.Identifier();
        if(idCtx != null){
          String accessSpecifier = idCtx.getText();
          String description = dict.get(accessSpecifier);
          descriptions.add(description);
        } 
        return visitChildren(ctx); 
    }

    @Override
    public List<String> visitMethodDeclaration(MethodDeclarationContext ctx){
        IdentifierContext idCtx = ctx.Identifier();
        if(idCtx != null){
          String accessSpecifier = idCtx.getText();
          String description = dict.get(accessSpecifier);
          descriptions.add(description);
        }
        return visitChildren(ctx);
    }

}

现在请注意,这个 class 不是很容易测试,管理 class 顶部的状态也是不可取的。但是,我很难想出一种方法来测试访问方法。 使用 Junit/Mockito,您可以执行以下操作:

public class JavaClassVisitorTest(){

  @Mock
  private ClassDeclarationContext classDecCtx;

  @Mock
  private IdentifierContext idCtx;

  @Before
  public void setup(){
     MockitoAnnotations.init(this);
  }    

  @Test
  public void test(){

     doReturn("public")
      .when(idCtx)
      .Identifier();

     doReturn(idCtx)
      .when(classDecCtx)
      .Identifier();

     JavaClassVisitor vstr = new JavaClassVisitor();
     vstr.visitClassDeclaration(classDecCtx);


  }

}

理想情况下,我想检查一下,例如,如果 idCtx 存在,是否添加了描述,但我无法使用此方法。对于我想要完成的事情,我是否持有错误的模式? 对如何更好地管理状态的任何见解都表示赞赏。

测试并不太难。

假设您想进行一些单元测试。然后你只需要查看方法实现并模拟其他所有内容。例如,visitClassDeclaration.

public void thatItProperlyCollectsDescriptionsForVisitedClassDeclarations() {
  // Given
  ClassDeclarationContext classDeclMock = mock(ClassDeclarationContext.class);
  JavaClassVisitor victim = spy(new JavaClassVisitor ());

  // When
  victim.visitClassDeclaration(classDeclMock)

  // Then
  assertTrue(victim.getDescriptions().contains(theExpectedString));  // I leave that to you :D
  verify(victim).visitChildren(classDeclMock);  // it calls the visit children method
}

我想您明白了:该方法需要向描述列表添加一些内容并调用 visitChildren 方法。其他人也一样。

对于集成测试,您可以构建一个测试 object,它是一个更完整的 Parse、Class 和方法声明上下文层次结构。但是我会把主要工作留在单元测试上,也许会用一个简单的模拟来测试一条快乐的道路,每个级别都有一个 child - 只是为了确保所有层次结构级别都被真正访问过。

(代码示例应该算是伪代码,我没有测试)