- PVSM.RU - https://www.pvsm.ru -

Как два программиста хлеб пекли

Как два программиста хлеб пекли

Я работаю программистом уже много лет, на протяжении которых, как это ни странно, я всё время что-то программирую. И вот какую интересную вещь я заметил: в коде, написанном мной месяц назад, всегда хочется что-то чуть-чуть поправить. В код полугодичной давности хочется поменять очень многое, а код, написанный два-три года назад, превращает меня в эмо: хочется заплакать и умереть. В этой статье я опишу два подхода. Благодаря первому архитектура программы получается запутанной, а сопровождение — неоправданно дорогим, а второй — это принцип KISS [1].

Итак, представим себе, что есть два программиста. Один из них умный, прочёл кучу статей на Хабре, знает каталог GoF [2] наизусть, а Фаулера [3] — в лицо. Другой же делает всё просто. Первого будут звать, например, Борис Н., а второго — Маркус П. Само собой, имена вымышленные, и все совпадения с реальными людьми и программистами случайны.

Итак, к ним обоим приходит проектный менеджер (если в вашей вселенной PM [4] не ходит сам к программистам, назовите его как-то иначе, например BA [5] или lead [6], сути это не изменит) и говорит:
— Ребята, нам нужно, чтобы делался хлеб.

Именно так, «делался», без уточнения способа производства.

Как же поступят наши программисты?

Как два программиста хлеб пекли

Борис создаёт свою первую абстракцию — класс Product, от него он наследует класс Bread, а инстанциирует экземпляры этого класса фабричный метод класса ProductFactory — createProduct().

Маркус делает примерно то же. Он создаёт класс Bread и класс Manager с фабричным методом createBread().

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

А сразу нельзя было сказать, что хлеб печётся не в вакууме, а в печке? Ну ладно, что же делают программисты?

Как два программиста хлеб пекли

Борис переименовывает класс ProductFactory в Oven, и выделяет абстракцию — AbstractOven. Чтобы было совсем красиво, он метод createProduct() переименовывает в bakeProduct(). Тем самым Борис в первый раз выполнил рефакторинг, применив «выделение абстракции», а так же реализовал шаблон «абстрактная фабрика» точь-в-точь как он описан в литературе. Молодец, Борис.

А вот Маркус ничего не делает. С его точки зрения всё и так хорошо. Ну может быть, стоит слегка поменяет реализацию createBread().

Фаза луны меняется, и менеджер в третий раз приходит к программистам. Он говорит:
— Нам нужно, чтобы печки были разных видов.

Что ж, справедливо.

Как два программиста хлеб пекли

Борис, радостно потирая руки, создаёт три наследника AbstractOven — ElectricOven, MicrowaveOven и GasOven. А класс Oven он удаляет за ненужностью.

Маркус тоже вносит изменения в программу. Он добавляет в метод createBread целочисленный параметр ovenType.

В четвёртый раз приходит к программистам менеджер. Он только что прочёл одну из книг серии «Я познаю мир». Интерференция новой информации и PMBoK [7] дала неожиданный результат. Менеджер говорит:
— Нам нужно, чтобы газовая печь не могла печь без газа.

Как два программиста хлеб пекли

Борис абсолютно безосновательно считает, что источник газа может быть только один. А для таких случаев всегда есть наш любимый шаблон [8]. Он создаёт одиночку GasSourceSingleton, а для уменьшения связность внедряет его через интерфейс GasSource в GasOven. Ура, он применил внедрение зависимости через сеттер!

Скромный от природы Маркус создаёт вещественное приватное поле gasLevel в классе Manager. Естественно, придётся чуть поменять логику метода createBread, но что поделаешь!

Но вот пару дней спустя менеджер приходит в пятый раз, и, сыто облизываясь, произносит:
— Нам нужно, чтобы печки могли выпекать ещё и пирожки (отдельно — с мясом, отдельно — с капустой), и торты.

Программисты тоже хотят есть, поэтому берутся за работу.

Как два программиста хлеб пекли

Борис уже начинает что-то такое чувствовать, но остановиться уже не может. Как печка узнает, что именно ей нужно готовить? Очевидно же — ей нужен повар. И Борис, не долго (а может и долго) думая, создаёт класс Cook. У него будет метод для приготовления, принимающий на вход абстрактную печь — cook(owen: AbstractOwen): Product. Ведь это логично — повар берёт печь, и с её помощью готовит. Потом Борис создаёт ещё несколько наследников класса Product — Cake и Pasty, а от Pasty наследует MeatPasty и CabbagePasty. А затем для каждого типа продукта создаёт отдельного повара — BreadCook, PastyCook и CakeCook.

Вроде ещё нормально, но времени на это ушло намного больше, чем у Маркуса, который просто добавил ещё один целочисленный параметр к методу createBread — breadType.

В шестой раз приходит менеджер. Кстати, то, что он сейчас попросит — это не требование заказчика, это его собственная инициатива. Но ведь об этом никто не узнает, так ведь?
— Нам нужно, чтобы хлеб, пирожки и торты выпекались по разным рецептам.

Как два программиста хлеб пекли

«Хм», — произносит Борис и вспоминает про шаблон «строитель» [9] (вместе со «свободным интерфейсом» [10], конечно же). Он создаёт класс Recipe, а к нему — строитель RecipeBuilder. Рецепт он внедряет (ВНЕЗАПНО!) в печку с помощью сеттера setRecipe(recipe:Recipe).

А Маркус (вы не поверите) добавляет ещё один целочисленный параметр в createBread — recipe.

Самое интересное, как всегда, происходит вдали от компьютеров. А именно: менеджер впервые после начала разработки встречается с заказчиком и наконец-то понимает, зачем тому нужна была печка. Он (менеджер) в седьмой раз приходит к программистам и говорит:
— Нам нужно, чтобы в печи можно было обжигать кирпичи.

Как два программиста хлеб пекли

Для Бориса это последняя встреча с менеджером, но всё же он из последних сил вносит изменения в архитектуру. Он выделяет абстрактный класс AbstractHeatingSmth — абстрактное нагревающее нечто. Для него он создаёт фабрику HeatingFactory. От AbstractHeatingSmth он наследует ProductOven и Furance. У последнего есть фабричный метод makeBrick, создающий экземпляр объекта Brick. Но ничего не работает. Читателю предлагается самостоятельно найти ошибку в архитектуре.

У Маркуса тоже не всё так гладко. Ему приходится создать уже третий (!) по счёту класс. Он называет его Brick, и добавляет в свой Manager метод makeBrick.

Конечно, можно возразить, что у Маркуса внутри метода createBread творится Адъ и Израиль [11], и это на самом деле так. Но с помощью шаблона «шаблонный метод» [12] беспорядок вполне можно структурировать. А в обилии фабрик и абстракций разобраться, ну, чуть сложнее.

Выводы, которые я хочу сделать, наверное, немного предсказуемы.

Подход Бориса хорош тем, что практически каждую часть системы можно изолировать и покрыть тестами. Но времени на создание такого количества классов уйдёт неприлично много, и каждое изменение требований обернётся каскадным изменением кода. Попытка же сделать архитектуру гибкой, предугадав пожелания заказчика, обычно проваливается — архитектура гнётся совсем не в том месте. Ведь, как известно «мир не просто удивительнее, чем мы себе представляем, —
он удивительнее, чем мы можем себе представить». И, получив очередной change request, программист убеждается в этом как никто другой.

Подход Маркуса, кончено, не позволяет использовать модульное тестирование, но зато он даёт результат намного быстрее, и изменения даются меньшей кровью. Этот подход — тот самый быстрый старт, которого так хотят стартаперы всех мастей. И, как ни странно, в таком коде действительно легче разобраться, потому что он проще.

А переписать всё заново, если что, — это всегда успеется.

Картинку взял отсюда [13]

Автор: ilichme


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/programmirovanie/16409

Ссылки в тексте:

[1] KISS: http://ru.wikipedia.org/wiki/KISS_%28%D0%BF%D1%80%D0%B8%D0%BD%D1%86%D0%B8%D0%BF%29

[2] каталог GoF: http://ru.wikipedia.org/wiki/Design_Patterns

[3] Фаулера: http://ru.wikipedia.org/wiki/%D0%A4%D0%B0%D1%83%D0%BB%D0%B5%D1%80,_%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD

[4] PM: http://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B5%D0%BD%D0%B5%D0%B4%D0%B6%D0%BC%D0%B5%D0%BD%D1%82

[5] BA: http://ru.wikipedia.org/wiki/%D0%91%D0%B8%D0%B7%D0%BD%D0%B5%D1%81-%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D1%82%D0%B8%D0%BA

[6] lead: http://ru.wikipedia.org/wiki/%D0%92%D0%B5%D0%B4%D1%83%D1%89%D0%B8%D0%B9_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%81%D1%82

[7] PMBoK: http://ru.wikipedia.org/wiki/PMBOK

[8] наш любимый шаблон: http://ru.wikipedia.org/wiki/Singleton

[9] «строитель»: http://ru.wikipedia.org/wiki/%D0%A1%D1%82%D1%80%D0%BE%D0%B8%D1%82%D0%B5%D0%BB%D1%8C_%28%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F%29

[10] «свободным интерфейсом»: http://ru.wikipedia.org/wiki/Fluent_interface

[11] Адъ и Израиль: http://lurkmore.to/%c0%e4%fa_%e8_%c8%e7%f0%e0%e8%eb%fc

[12] «шаблонный метод»: http://ru.wikipedia.org/wiki/%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4_%28%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F%29

[13] отсюда: http://fotoanaliz.hurriyet.com.tr/galeridetay.aspx?P=2&cid=35509&rid=4369