2015-03-03 7 views
1
void executeRequests() { 

    ... 

    for (Request request in requests) { 
     if (request is Type1Request) { 
      ... 
     } else if (request is Type2Request) { 
      ... 
     } else if (...) { 

     } ... 
    } 

} 

В основном у меня есть метод, который выполняет некоторый общий процесс для списка запросов, а затем перебирает этот список и на основе типа запроса (его конкретного класса) выполняет определенную обработку.Зачем мне в этом случае разделять проблемы?

Можно было бы утверждать, что я должен разделить каждый if else блок в отдельные классы, которые имеют execute метод, а затем просто вызвать execute внутри for loop, потому что это нарушает Separation of concerns узора. И я согласен! Я просто не знаю почему.

В этом случае допустим, что я никогда не буду повторно использовать эти классы или частные функции (или, тем не менее, я реорганизую). Некоторые люди утверждают, что этот способ легче читать, потому что вам не нужно переходить на разные файлы или работать, чтобы все понять.

Так что мой вопрос: почему этот фрагмент кода плохой?

спасибо.

+0

Если вы хотите получить подробное объяснение этих концепций, я настоятельно рекомендую вам прочитать [Clean Code by Robert C. Martin] (http://www.amazon.ca/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882). Вы также можете взглянуть на [этот пост в блоге] (http://blog.8thlight.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html) от того же автора. –

ответ

1

Проще говоря, потому что добавление объекта Type3Request в ваш код заставляет вас редактировать существующий код. Каждый раз, когда вы меняете существующий код, вы должны протестировать все связанные модули в своем приложении, чтобы убедиться, что в коде отсутствуют ошибки регрессии. В javascript или других свободно типизированных языках очень легко переписать глобальные данные из ветви if. На большинстве строго типизированных языков вы можете вообще избежать этой проблемы, но есть все еще возможность перезаписи локальных данных. Это означает, что каждый раз, когда вы меняете этот метод executeRequests, вы должны запускать все возможные запросы в своем приложении, чтобы убедиться, что вы не представили новую проблему. Я хочу пояснить, что я сказал все запросы, а не все типы запросов. Это потенциально тысячи тестов для запуска, даже если у вас есть только несколько типов запросов.

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

Используя полиморфизм, весь «конкретный код», который должен быть выполнен для этого запроса, будет не только инкапсулирован в объект Type3Request, но и на самом деле придет с этим объектом. Для добавления нового типа запроса нет другого файла кода.

Это означает, что вы можете предоставлять новые типы запросов динамически в виде плагинов, просто добавляя DLL в папку и не перекомпилируя существующий код. Если вы не хотите идти так далеко, он по-прежнему дает вам возможность лучше разделить рабочую нагрузку между всеми членами команды, избегая слияний с этим методом executeRequests.

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

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

+0

В чем проблема редактирования существующего кода? Кто-то может утверждать, что конкретный код инкапсулируется в блок «else if». Я не очень беспокоюсь о специфике языка, давайте предположим, что я использую интерпретируемый язык. Слияния - хороший аргумент, но я все еще могу разделить работу как разные типы запросов, тогда слияние просто должно было принять оба изменения. Некоторые люди (а не я) считают, что тестирование - пустая трата времени, поэтому я не хочу использовать это в качестве аргумента. В любом случае, почему было бы проще протестировать? Я все еще могу создать тестовый метод для каждого «else if». –

+0

Этот последний аргумент кажется в пользу «if else», сохраняя весь код в одном файле, что облегчает поиск всего кода, связанного с запросом. –

+0

Я улучшил ответ, чтобы покрыть ваши комментарии. –

0

Какое действие связано с конкретным запросом, не связано с объектом, который выполняет конкретный запрос из-за какого-либо внешнего триггера. Озабоченность (обработка последствий) принадлежит запросу. Таким образом, вы отделяете его от исполнителя, потому что он есть неправильно. Что-то внутреннее (действие определенного типа) «передается на аутсорсинг» другому классу, и класс доминирует над этим решением, что является ответственностью и связью.

Это не вопрос того, почему вы должны это делать. Это наоборот ... как это могло быть иначе? Если мы выполняем правильное объектно-ориентированное программирование.

+0

Все верно, но я хочу знать конкретную причину того, почему это сочетание проблем плохо. Почему объектно-ориентированное программирование лучше? –

+0

Поскольку вы хотите, чтобы ваши приложения обладали предсказуемой надежной архитектурой. Сочетание умножает (я не знаю даже скорость экспоненциальности). Возможные пути для отказа, делает код более сложным для отладки, модульного теста, понимания и изменения. –

 Смежные вопросы

  • Нет связанных вопросов^_^