Вопрос
Краткая предыстория:Я программирую на PHP, у меня есть модель предметной области с отдельным уровнем доступа к данным (классы DAO), которые отвечают за выборку данных из базы данных и создание классов предметной области.
Допустим, у меня есть класс DAO, ответственный за создание Группа и Групповой список Объекты.Вы можете представить группы как компонент социальной сети;хотя на самом деле не имеет значения, что это такое для этого вопроса.
Мне нужно уметь просить ДАО сделать меня другим Групповой список объекты, основанные на различных критериях:
- самые последние добавленные группы
- самые популярные группы
- группы, определенные администратором как "рекомендуемые"
- группы, помеченные определенным тегом
- группы, соответствующие определенному ключевому слову
- группы внутри определенной категории
- группы, созданные определенным человеком
- группы , созданные в определенный день
Некоторые из них мне на самом деле не нужны прямо сейчас, но я могу себе представить, что они мне понадобятся до завершения проекта.Теперь я начал с приятного простого метода DAO: Создатель.Это сработало отлично.Вы можете думать о псевдокоде как:
find out how many groups
create SQL query to fetch group details
loop through results
{
create group object
add to group list object
}
По мере развития моего приложения я затем создал новый метод Создать список избранных.Это сработало отлично.Но на самом деле это было очень - очень похоже на Создатель, с немного другим запросом.Большая часть остальных ~ 150 строк кода были идентичны.
Итак... что мне следует делать со всеми немного отличающимися случаями, которые мне нужны? Большую часть времени я действительно просто хочу отфильтровать и отсортировать список на основе определенных критериев.Вопрос в следующем - должен ли я:
а) создавайте множество целенаправленных методов творчества, таких как:
- Список создателей()
- Создать список категорий ( categoryObject )
- Создайте список пользователей ( UserObject )
- Создать список тегов ( tag )
- createPopularList () Создать популярный список ()
или
б) создайте один БОЛЬШОЙ метод, который может делать все:- Список создателей ( строка поиска, OrderBy, filterByCategoryObject=null, filterByUserObject=null )
Мне очень нравится идея (a), потому что мой интерфейс DAO проще и с меньшей вероятностью нуждается в изменении (например:добавление другого параметра, когда мне внезапно нужно передать в Дата для сравнения) Сложность возникает, когда у вас есть такие вещи, как ключевые слова для поиска, которые вы хотите иметь возможность комбинировать с другими параметрами;например:поиск по списку категорий, поиск по популярному списку, поиск по списку тегов и т.д...(a) - это своего рода то, с чего я начал.
У меня был флирт с рефакторингом в (b), но я вижу, что мой метод довольно быстро становится очень большим и очень сложным, много "if" и "select", чтобы иметь дело с различными случаями при построении SQL, и множество параметров, которые вводятся в метод.Но, по крайней мере, все это в одном месте.И вы можете комбинировать вещи;например:группы пользователей, помеченные бла-бла, совпадающие ключевые слова бла-бла.
Решение
Вы могли бы создать частный метод, к которому обращаются все общедоступные методы.IE
private function _createList ( searchString, orderBy, ... )
{
...
}
public function createList()
{
return $this->_createList('...', 'id');
}
public function createCategoryList()
{
return $this->_createList('...', 'category_id');
}
Таким образом, если вашу функцию _createList потребуется изменить позже, вам нужно будет рефакторинговать только общедоступные методы в этом DAO, а не все классы, которые используют этот DAO.
Другие советы
Я не думаю, что это строго ситуация "или-или".Вариант a - это приятный удобный интерфейс для вашего DAO, поэтому я думаю, вам следует сохранить его.Мне вариант b действительно кажется логикой, специфичной для реализации.Поэтому, если БОЛЬШОЙ метод подходит для ваших целей, я бы посоветовал использовать его для выполнения фактической логики обработки при одновременном предоставлении интерфейса, как в варианте a.
Тем не менее, если БОЛЬШОЙ метод становится слишком запутанным и сложным, а повторное использование кода фактически увеличивает сложность вашего кода и снижает ремонтопригодность приложения, тогда вы можете провести рефакторинг, чтобы сохранить отдельные инструкции SQL для каждого метода интерфейса, но заставить вспомогательный метод выполнять общую логику анализа результатов.
В большой метод в варианте B почти гарантированно сокращает повторное использование кода и увеличивает сложность и время обслуживания.
Лично (и согласно Code Complete) методы должны делать что-то одно, и делать это хорошо, а не пытаться втиснуть в себя все подряд.Избегайте необходимости рефакторинга в будущем и делайте это с умом в первый раз.
Основы программирования:хорошей практикой является разбивать ваши коды на разделы или функции.
Я остановлюсь на варианте (а);Вам нужно будет поддерживать и отлаживать код.И когда у вас действительно обнаружится ошибка, вы будете очень рады, что разделили свой код на различные методы.
Кроме того, написание имени метода помогает вам понять, что вы делаете.
сравните это:
Вариант (а)
$obj->AddNewList( /* params */ );
$obj->UpdateList( /* params */ );
и это:
Вариант (b)
$obj->parse( /* first set of params */ );
$obj->parse( /* second set of params */ );
Это экономит время, поскольку люди читают слева направо.Вот почему имена функций и методов всегда находятся слева.
Если либо производительность не является серьезной проблемой, либо группы меняются достаточно медленно, чтобы кэширование запросов было полезным, вы могли бы написать функции фильтрации и передать ихв.Если вы перебираете группы и у вас есть необязательный массив методов фильтрации, ваш цикл станет:
for(group in group) {
cont = true
for(f in functions) {
if ! f(group) {cont = false; continue;}
}
if(cont) Continue
add group to list
}
Это позволило бы вам изменять параметры фильтрации, не изменяя цикл, просто записывая или изменяя функцию.