Допустим, у нас есть сайт с товарами. У нас есть метод Product::getProducts
— возвращает массив товаров.
Затем к нам пришел менеджер и сказал: хотим сделать акции на сайте (скидки, распродажи). В каждой акции будут прикреплены товары. Мы написали метод Action::getProductsByActionId(actionId)
Затем к нам снова пришел менеджер и сказал что нужны еще статьи, к которым прикреплены товары. Добавим метод Article::getProductsByArticleId(arcticleId)
.
Вы скажите — давайте сделаем 1 метод с параметрами. Ок, мы до этого дойдем.
И тут внимание: к нам пришел менеджер и говорит — мы хотим иметь возможность отключать товары на сайте. Т.е. необходимо добавить флаг active для товаров, имеющий значение true/false. Товары с флагом false не должны отображаться на сайте.
Итог: нам надо найти в коде все места, которые достают товары и везде добавить логику, чтобы возвращались только товары с флагом active=true. Т.е. надо поправить 3 метода: Product::getProducts,
Action::getProductsByActionId(actionId),
Article::getProductsByArticleId(arcticleId)
То, что нам надо поправить код не в 1 месте, порождает ошибки — в каком-то месте забыли поправить.
Что же, давайте сделаем так, чтобы у нас было только 1 место, откуда доставать товары. Тогда такой ошибки не будет.
Опять та же ситуация: достаем товары для основного каталога сайта, акций и статей: пишем метод Product::getProducts(actionId = null, articleId = null)
. Если не задан ни один параметр, то достаются товары как в Product::getProducts
раньше, если задан articleId, то достаются товары Action::getProductsByActionId(actionId)
, и т.д.
Пока остановимся на том моменте, что мы не включали флаг активации товаров, т.е. менеджер с таким требованием не приходил.
Приходит менеджер и говорит: хотим показывать товары в складской программе. Ок. Мы уже поняли, что будет, если мы добавим метод Store::getProducts
— в какой-то момент доработки программы мы можем просто про него забыть, и не внести в него необходиму логику. По этому переиспользуем метод Product::getProducts
. На складе должны показываться абсолютно все товары (вообще всегда все, это может быть не складская программа, а какая-нибудь админка или другая бек-система для персонала), по этому мы вызываем этот метод без параметров.
И тут внимание: приходит менеджер и говорит: хотим сделать активные и неактивные товары, флаг active. Чтобы на сайте показывались только активные товары. Ок. У нас есть 1 метод — Product::getProducts
и мы правим его и добавляем чтобы из него возвращались только товары с флагом active=true.
Мы вносим изменения в единственный метод. И опять — нам надо посмотреть везде где этот метод используется. И если в 1 месте мы забыли посмотреть, то это приведет к ошибке — на складе пропадает часть товаров. Хотя там по ТЗ должны отображаться вообще все товары. Мы просто забыли, что наш универсальный метод Product::getProducts
используется еще и в бек-системах, где флаг active не должен применяться к товарам. Получается при написании 1 метода тоже возникает ошибка.
Вывод
Получается что написать 1 метод — это приведет в определенной ситуации к ошибке, и написать несколько методов — это приведет в другой ситуации к ошибке. Получается сколько методов не напиши — всегда когда-нибудь, при изменении бизнес-требований будет ошибка. Получается программист решает нерешаемую без ошибок задачу. Тогда становится понятно, почему идеального кода — не бывает.
UPD:
В комментариях указали, что если сделать 1 метод: Product::getProducts, и затем при изменении в нем назвать его например Product::getProductsV2, а Product::getProducts вообще запретить вызывать, то тогда тогда мы точно найдем все места, где этот метод вызывался, и исключим ошибку.
Второй способ: создать специализированный класс, содержащий 1 private функцию Product::getProducts. И в том же классе создать кучу public методов, на каждый функционал свою функцию, т.е. для каталога - Product::getProductsForCatalog, для акций - Product::getProductsForAction, для статей: Product::getProductsForArticles, Product::getProductsForStore и т.д. Главное условие - чтобы вызов каждой функции был только из 1 места, и все функции находились в 1 классе. Тогда просмотрев лишь 1 класс (это сильно упрощает задачу), мы сразу сможем понять где используется наша функция.
Anton238
Конечно, идеального кода не бывает. Всегда нужно менять старые подходы или архитектуру, переписывая кучу уже написанного кода.
Однако отличие хорошей архитектуры и хорошего кода от плохой архитектуры и плохого кода — сколько сил вам на это нужно потратить.
Условно, если у вас говнокод и архитектура построена на костылях, и вам нужно что-то поменять — для вас это будет непросто и займет очень много времени.
Однако если у вас хорошая, гибкая архитектура и чистый код, то запилить какую нибудь фичу, которая требует изменений архитектуры, займет гораздо меньше времени.