Java

时间:2017-01-24 16:19:18

标签: java oop refactoring class-design

我有一个名为TestExecutor的Java类,负责starting测试。开始测试涉及多个阶段:

- Update test repository
- Locate the test script
- Create result empty directory
- Execute command
- Parse output
- Update database

对于每个阶段,我在TestExecutor类中创建了私有方法,它们执行上面的每个操作,所有操作都包含在try-catch块中。我知道这不是一个好设计,因为我的课程做得太多,并且由于私有方法中隐藏了大量功能,因此单元测试也很痛苦。

我希望听到您重构此课程的建议,因为我不确定如何摆脱类似于上述结构的内容。代码示例如下:

public void start() throws TestExecuteException {
    try {
        updateRepository();
        locateScript();
        createResultDirectory();
        executeCommand();
        parseOutput();
        updateDatabase();
    catch(a,b,c) {
    }
}

private updateRepository() {
    // Code here
}
// And repeat for other functions

3 个答案:

答案 0 :(得分:0)

你的班级对我来说还不错。

  

我知道这不是好设计,因为我的班级做得太多了

就班级负有单一责任而言,方法的数量并不重要。

检查template method design pattern。您的类正在执行类似于抽象Game类正在执行的操作。

public abstract class Game {
   abstract void initialize();
   abstract void startPlay();
   abstract void endPlay();

   //template method
   public final void play(){

      //initialize the game
      initialize();

      //start game
      startPlay();

      //end game
      endPlay();
   }
}
  

并且由于大量功能而对单元测试也很痛苦   隐藏在私人方法中

阅读this&关于测试私有方法的this。您还可以使用PowerMock之类的框架来帮助您测试不可测试的代码。

答案 1 :(得分:0)

  

我想听听你重构这个课程的建议,因为我不知道如何摆脱类似于上述结构的东西

您绝对应该将SOLID原则作为编写干净,可测试的面向对象代码的起点。我在职业生涯的开始就被介绍过,并且遵循这些原则确实很有帮助。

那就是说,我首先将相关功能分组到不同的类中。例如,updateRepository()updateDatabase()可以移动到名为DatabaseHelper的单独类中。同样地,locateScript()createResultDirectory()似乎是与磁盘相关的操作,可以移动到名为DirectoryHelper的单独类。我相信你得到了它的要点。你刚刚取得的成就是关注的分离

现在您已经有了单独的类,您需要将它们组合在一起并将它们投入使用。您的TestExecutor可以继续使用您列出的methods。唯一不同的是,这些方法现在将他们的工作委托给我们在上面创建的各个类。为此,TestExecutor需要引用DatabaseHelperDirectoryHelper类。您可以直接在TestExecutor内实例化这些类。但这意味着TestExecutor与实现紧密耦合。您可以执行的操作是询问TestExecutor之外的代码,以提供要使用的DatabaseHelpeDirectoryHelper。这称为依赖性反转依赖注入。这种方法的优点是您现在可以将DatabaseHelperDirectoryHelper的任何子类传递给TaskExecutor,并且不必了解实现的详细信息。通过模拟这些依赖关系而不是传递实际实例,这有助于TaskExecutor的单元测试。

我将留下其他SOLID原则供您探索,实施和欣赏。

答案 2 :(得分:0)

我会这样做。首先,执行每个测试步骤应该具有的合同。

interface TestCommand{
  void run();
}

现在将您的测试命令作为单独的类。尝试使这些命令类具有通用性,以便您可以重用相似类型的命令。现在,在您要运行测试的类中,配置测试步骤如下。

//in your test class do this.
List<TestStep> testCommands = new ArrayList<>();
testCommands.add(new UpdateRepoCommand());
testCommands.add(new LocateScriptCommand());
// and so on....

现在,按时间顺序执行所有步骤。

public void start(testSteps) throws TestExecuteException {
    try {
        for(TestCommand command : testCommands){
           command.run()
    }        
    catch(Exception e) {
      //deal with e
    }
}

此外,正如上述CKing所述,在这些测试步骤中遵循SOLID原则。注入依赖项并分别为它们编写单元测试。