2017-01-24 33 views
0

У меня есть класс Java TestExecutor, который отвечает за starting тест. Запуск теста включает в себя ряд этапов:Хороший пример объектно-ориентированного проектирования в Java

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

Для каждого из этих этапов я создал частные методы в классе TestExecutor, которые выполняют каждую из указанных выше действий, все окруженное в примерочных поймать блок. Я знаю, что это не очень хороший дизайн, так как мой класс слишком много, и это также боль для модульного теста из-за большого количества функций, скрытых в частных методах.

Я хотел бы услышать ваши предложения по рефакторингу этого класса, так как я не уверен, как уйти от чего-то подобного вышеуказанной структуре. Пример кода ниже:

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 
+0

Почему ваши методы не являются публичными? Нет никакой проблемы при наличии нескольких методов в одном классе, если класс несет одну ответственность. – underdog

+0

Нет причин для того, чтобы методы вызывались чем-либо внешним для TestExecutor, поэтому я решил сделать их частными. Моя проблема заключается в том, что класс несет общую ответственность (выполнение теста), однако под ним есть несколько обязанностей (обновить реестр, найти скрипт и т. Д.) – Adam

+0

@Adam Просьба получить ответы, которые были предоставлены вам, и прокомментировать дальнейшие разъяснения , Если какой-либо из ответов был полезен, вы можете повысить их. Вы также можете принять ответ, нажав галочку рядом с ответом, который был наиболее полезным. Пожалуйста, прочитайте [Что делать, если кто-то отвечает на мой вопрос] (http://stackoverflow.com/help/someone-answers). Голосование бесплатное. Это ничего не стоит. Принятие ответа дает вам +2 репутации. Так что не стесняйтесь. Иди вперед и заплати! – CKing

ответ

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(); 
    } 
} 

, а также боль модульного тестирования в связи с большим количеством функциональных возможностей будучи спрятаны в частных методах

Read this & this о тестировании частных методов. Вы также можете использовать фреймворк вроде PowerMock, который поможет вам в тестировании непроверяемого кода.

+0

У класса, похоже, много обязанностей. 1) Поговорите с базой данных. 2) Поговорите с каталогами. 3) Разбирайте данные и создавайте выходные данные. – CKing

0

Я хотел бы услышать ваши предложения для рефакторинга этого класса как я не знаю, как избавиться от чего-то похожего на приведенную выше структуру

Вы обязательно должны взглянуть на SOLID принципы как отправная точка для написания чистого, тестируемого объектно-ориентированного кода. Я познакомился с ним в начале моей карьеры, и это действительно помогает многим следовать этим принципам.

Сказанное, я бы начал с группировки связанных функций в разные классы. Например, updateRepository() и updateDatabase() могут быть перемещены в отдельный класс под названием DatabaseHelper. Аналогично locateScript() и createResultDirectory(), похоже, связаны с диском и могут быть перемещены в отдельный класс под названием DirectoryHelper. Полагаю, вы поняли суть этого. То, что вы только что достигли, было Отвлечение внимания.

Теперь, когда у вас есть отдельные классы, вам нужно собрать их вместе и заставить их работать. Ваш TestExecutor может по-прежнему иметь methods, который вы указали. Единственное отличие заключается в том, что эти методы теперь делегируют свою работу отдельным классам, которые мы создали выше. Для этого TestExecutor потребуется ссылка на классы DatabaseHelper и DirectoryHelper.Вы можете просто создать эти классы непосредственно внутри TestExecutor. Но это будет означать, что TestExecutor тесно связан с реализацией. Вместо этого вы можете запросить код за пределами TestExecutor для подачи DatabaseHelpe и DirectoryHelper. Это известно как Инверсия зависимостей до Инъекция зависимостей. Преимущество такого подхода состоит в том, что теперь вы можете передать любой подкласс DatabaseHelper и DirectoryHelper до TaskExecutor, и ему не нужно знать подробности реализации. Это облегчает модульное тестирование TaskExecutor путем смешения этих зависимостей вместо передачи фактических экземпляров.

Я оставлю остальную часть принципов SOLID для вас, чтобы изучить, внедрить и оценить.

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 
    } 
} 

Кроме того, как описано выше, следуйте принципам SOLID внутри этих этапов тестирования. Зависимости от инжекции и написать для них единичный тест отдельно.