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

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

Я працюю програмістом вже багато років, протягом яких, як це не дивно, я весь час щось програмую. І ось яку цікаву річ я помітив: у коді, написаному мною місяць тому, завжди хочеться щось трохи поправити. У код піврічної давності хочеться поміняти дуже багато, а код, написаний два-три роки тому, перетворює мене в емо: хочеться заплакати і померти. У цій статті я опишу два підходи. Завдяки першому архітектура програми виходить заплутаною, а супровід - невиправдано дорогим, а другий - це принцип KISS.

Отже, уявімо собі, що є два програміста. Один з них розумний, прочитав купу статей на Хабре, знає каталог GoF напам'ять, а Фаулера - в обличчя. Інший же робить все просто. Першого зватимуть, наприклад, Борис Н. а другого - Маркус П. Само собою, імена вигадані, і всі збіги з реальними людьми і програмістами випадкові.

Отже, до них обох приходить проектний менеджер (якщо у вашій всесвіту PM не ходить сам до програмістам, назвіть його якось інакше, наприклад BA або lead. Суті це не змінить) і каже:
- Хлопці, нам потрібно, щоб робився хліб.

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

Як же надійдуть наші програмісти?

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

Борис створює свою першу абстракцію - клас Product, від нього він успадковує клас Bread, а інстанціірует екземпляри цього класу фабричний метод класу ProductFactory - createProduct ().

Маркус робить приблизно те ж. Він створює клас Bread і клас Manager з промисловим способом createBread ().

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

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

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

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

А ось Маркус нічого не робить. З його точки зору все і так добре. Ну може бути, варто злегка змінить реалізацію createBread ().

Фаза місяця змінюється, і менеджер в третій раз приходить до програмістам. Він говорить:
- Нам потрібно, щоб грубки були різних видів.

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

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

Борис, радісно потираючи руки, створює три спадкоємця AbstractOven - ElectricOven, MicrowaveOven і GasOven. А клас Oven він видаляє за непотрібністю.

Маркус теж вносить зміни в програму. Він додає в метод createBread цілочисельний параметр ovenType.

У четвертий раз приходить до програмістам менеджер. Він тільки що прочитав одну з книг серії «Я пізнаю світ». Інтерференція нової інформації і PMBoK дала несподіваний результат. Менеджер каже:
- Нам потрібно, щоб газова піч не могла піч без газу.

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

Борис абсолютно безпідставно вважає, що джерело газу може бути тільки один. А для таких випадків завжди є наш улюблений шаблон. Він створює поодинці GasSourceSingleton, а для зменшення зв'язність впроваджує його через інтерфейс GasSource в GasOven. Ура, він застосував впровадження залежності через сетер!

Скромний від природи Маркус створює речовий приватне поле gasLevel в класі Manager. Природно, доведеться трохи змінити логіку методу createBread, але що поробиш!

Але ось пару днів по тому менеджер приходить в п'ятий раз, і, сито облизуючись, вимовляє:
- Нам потрібно, щоб грубки могли випікати ще й пиріжки (окремо - з м'ясом, окремо - з капустою), і торти.

Програмісти теж хочуть їсти, тому беруться за роботу.

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

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

Начебто ще нормально, але часу на це пішло набагато більше, ніж у Маркуса, який просто додав ще один цілочисельний параметр до методу createBread - breadType.

У шостий раз приходить менеджер. До речі, те, що він зараз попросить - це не вимога замовника, це його власна ініціатива. Але ж про це ніхто не дізнається, чи не так?
- Нам потрібно, щоб хліб, пиріжки і торти випікалися за різними рецептами.

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

«Хм», - вимовляє Борис і згадує про шаблон «будівельник» (разом з «вільним інтерфейсом». Звичайно ж). Він створює клас Recipe, а до нього - будівельник RecipeBuilder. Рецепт він впроваджує (РАПТОМ!) В грубку за допомогою сетера setRecipe (recipe: Recipe).

А Маркус (ви не повірите) додає ще один цілочисельний параметр в createBread - recipe.

Найцікавіше, як завжди, відбувається далеко від комп'ютерів. А саме: менеджер вперше після початку розробки зустрічається з замовником і нарешті розуміє, навіщо йому потрібна була піч. Він (менеджер) в сьомий раз приходить до програмістам і каже:
- Нам потрібно, щоб в печі можна було обпалювати цеглу.

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

Для Бориса це остання зустріч з менеджером, але все ж він з останніх сил вносить зміни в архітектуру. Він виділяє абстрактний клас AbstractHeatingSmth - абстрактне нагріває щось. Для нього він створює фабрику HeatingFactory. Від AbstractHeatingSmth він успадковує ProductOven і Furance. В останнього є фабричний метод makeBrick, що створює екземпляр об'єкта Brick. Але нічого не працює. Читачеві пропонується самостійно знайти помилку в архітектурі.

У Маркуса теж не все так гладко. Йому доводиться створити вже третій (!) За рахунком клас. Він називає його Brick, і додає в свій Manager метод makeBrick.

Звичайно, можна заперечити, що у Маркуса всередині методу createBread твориться ад та Ізраїль. і це насправді так. Але за допомогою шаблону «шаблонний метод» безлад цілком можна структурувати. А в достатку фабрик і абстракцій розібратися, ну, трохи складніше.

Висновки, які я хочу зробити, напевно, трохи передбачувані.

Підхід Бориса хороший тим, що практично кожну частину системи можна ізолювати і покрити тестами. Але часу на створення такої кількості класів піде непристойно багато, і кожна зміна вимог обернеться каскадним зміною коду. Спроба ж зробити архітектуру гнучкою, передбачивши побажання замовника, зазвичай провалюється - архітектура гнеться зовсім не в тому місці. Адже, як відомо «світ не просто дивніше, ніж ми собі уявляємо, -
він дивніше, ніж ми можемо собі уявити ». І, отримавши черговий change request, програміст переконується в цьому як ніхто інший.

Підхід Маркуса, скінчено, не дозволяє використовувати модульне тестування, але зате він дає результат набагато швидше, і зміни даються меншою кров'ю. Цей підхід - той самий швидкий старт, якого так хочуть стартаперів всіх мастей. І, як не дивно, в такому коді дійсно легше розібратися, тому що він простіше.

А переписати все заново, якщо що, - це завжди встигну.

Схожі статті