Чистий код: наслідки коротких методів з малою кількістю параметрів


15

Нещодавно під час огляду коду я натрапив на код, написаний новим колегою, який містить візерунок із запахом. Я підозрюю, що рішення мого колеги ґрунтуються на правилах, запропонованих відомою книгою «Чистий кодекс» (а можливо, і іншими подібними книгами).

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

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

Я підозрюю, що цей код був натхненний порадою тримати методи короткими (<= 5 рядків коду), уникати великих списків параметрів (<3 параметри) і конструктори не повинні працювати (наприклад, виконуючи якийсь обчислення). що важливо для обґрунтованості об'єкта).

Тепер, звичайно, я можу скласти справу проти цієї закономірності, якщо можу довести, що всі види невизначених помилок потенційно виникають, коли методи не викликаються в певному порядку. Однак я прогнозую, що відповідь на це буде додавати перевірки, які підтверджують, що властивості повинні бути встановлені після виклику методів, які потребують встановлення цих властивостей.

Проте я б запропонував повністю змінити код, щоб клас став блакитним друком на фактичний об'єкт, а не на низку методів, які слід викликати (процедурно) у визначеному порядку.

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


6
1. Гра на захисника диявола на мить ... Чи справді код працює? Тому що Об'єкти передачі даних - це абсолютно дійсна методика, і якщо це все, це ...
Роберт Харві

7
2. Якщо вам не вистачає слів, щоб описати проблему, у вас не вистачає досвіду, щоб спростувати позицію колеги.
Роберт Харві

4
3. Якщо у вас є якийсь робочий код, який ви можете опублікувати, опублікуйте його в Code Review і дозвольте їм ознайомитися. Інакше це просто мандрівна загальність.
Роберт Харві

5
@RobertHarvey "Значення, отримані в результаті обчислень, присвоєні властивостям, які використовуються в декількох приватних методах у всьому класі", для мене не звучать як DTO, що поважає себе. Я згоден, що трохи більше конкретності було б корисним.
Топо Відновити Моніку

4
Убік: Звучить, ніби хтось насправді не прочитав Чистий код перед тим, як ударити його. Я просто знову просканував його і не зміг знайти місця, де це підказує "конструктори не повинні працювати" (деякі приклади насправді виконують роботу), і пропоноване рішення для уникнення занадто великої кількості параметрів полягає у створенні об'єкта параметрів, що консолідує пов'язане групи параметрів, а не бастардізуйте свої функції. І книга дійсно запропонувати рефакторінга коду , щоб уникнути часових залежностей між методами. Я думаю, що ваша упередженість щодо декількох його бажаних стилів коду покращила ваше сприйняття книги.
Ерік Кінг

Відповіді:


13

Оскільки хтось, хто читав чистий код і переглядав серію «Чисті кодери», багато разів і часто навчає та тренує інших людей писати більш чистий код, я дійсно можу поручити, що ваші спостереження правильні - показники, які ви вказуєте, згадуються в книзі .

Однак у цій книзі йдеться про інші моменти, які також слід застосовувати поряд із рекомендаціями, які ви вказали. Вони, здавалося б, були проігноровані в коді, яким ви маєте справу. Це могло статися тому, що ваш колега ще перебуває у фазі навчання, і в такому випадку, наскільки потрібно вказати на запахи їх коду, добре пам’ятати, що вони роблять це з доброї волі, навчаючись і намагаючись написати кращий код.

Чистий кодекс пропонує, що методи повинні бути короткими, з можливістю якомога менше аргументів. Але в рамках цих керівних принципів пропонується дотримуватися принципів S OLID, підвищувати згуртованість та зменшувати зв'язок .

S в ТВЕРДИХ трибунами на єдиної відповідальності принцип, який говорить , що об'єкт повинен нести відповідальність тільки одну річ. "Річ" - не дуже точний термін, тому описи цього принципу сильно різняться. Однак дядько Боб, автор «Чистого кодексу», також є людиною, яка придумала цей принцип, описуючи його так: «Зберіть разом речі, які змінюються з тих же причин. Розділіть ті речі, які змінюються з різних причин». Він продовжує говорити, що він має на увазі з причин змінитись тут і тут(довше пояснення тут було б занадто багато). Якщо цей принцип був застосований до класу, з яким ви маєте справу, велика ймовірність, що фрагменти, які стосуються обчислень, будуть відокремлені від тих, що стосуються стану утримання, шляхом поділу класу на два чи більше, залежно від того, скільки причин змінити ці розрахунки доведеться.

Також класи Clean повинні бути згуртованими , це означає, що більшість його методів використовують більшість своїх атрибутів. Як такий, максимально згуртований клас - це той, де всі методи використовують увесь його атрибут; Наприклад, у графічному додатку у вас може бути Vectorклас з атрибутами Point aі Point b, де єдиними методами є, scaleBy(double factor)і printTo(Canvas canvas)обидва працюють на обох атрибутах. Навпаки, мінімально згуртований клас - це той, де кожен атрибут використовується лише в одному методі, і ніколи не використовується більше одного атрибута кожним методом. У середньому, клас представляє некогезивні "групи" згуртованих частин - тобто декілька методів використовують атрибути a, bа c, а решта використовують cіd - це означає, що якщо ми розділимо клас на два, ми закінчимось двома згуртованими об’єктами.

Нарешті, заняття « Очищення» повинні максимально зменшити зв'язок . Хоча тут існує багато типів з’єднання, про які варто обговорити, здається, що код в основному страждає від тимчасового з’єднання , де, як ви вказали, методи об'єкта працюватимуть, як очікувалося, тоді, коли вони викликаються у правильному порядку. І як і два вищезазначені вказівки, рішення цього зазвичай передбачають розбиття класу на два або більше об'єднаних об'єкта. Стратегія розщеплення в цьому випадку зазвичай включає моделі, такі як Builder або Factory, а у дуже складних випадках - State-Machines.

TL; DR: Правила чистого кодексу, яких дотримувався ваш колега, є хорошими, але лише тоді, коли також дотримуються решти принципів, практики та зразків, згаданих у книзі. Чистий версія «клас» Ви бачите , буде розбито на кілька класів, кожен з однією відповідальністю, згуртованими методами і без тимчасової зв'язку. Це той контекст, де маленькі методи і малопомітні аргументи мають сенс.


1
І ви, і топо морто написали хорошу відповідь, але я можу прийняти лише одну. Мені подобається, що ви зверталися до СРП, згуртованості та зв'язку. Це корисні терміни, які я можу використовувати в огляді коду. Розбиття об’єкта на більш дрібні об'єкти з власними обов'язками - це очевидно шлях. Один (не конструкторський) метод, який ініціалізує значення на купі властивостей класу, є мертвим подачею, який слід повернути новому об'єкту. Я мав це бачити.
користувач2180613

1
SRP - це найважливіший орієнтир; одне кільце, щоб керувати ними всіма і всім цим. Добре зроблена SRP, природно, призводить до коротших методів. Приклад: Я маю передній клас з 2 лише загальнодоступними та приблизно 8 непублічними методами. Жоден не більше ~ 3 рядків; усього класу близько 35 LOC. Але я писав цей клас останнім часом! На той час, коли був написаний весь базовий код, цей клас по суті писав сам, і я не повинен був, дійсно, не міг зробити методи більші. Я ніколи не сказав: "Я збираюся написати ці методи в 5 рядків, якщо це вб'є мене". Кожен раз, коли ви застосовуєте SRP, це просто відбувається.
radarbob

11

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

Зазвичай це відповідає за приведення об'єкта в початковий дійсний стан, так; інші властивості або методи можуть потім змінити стан на інший дійсний стан.

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

Окрім проблем з читабельністю та ремонтопридатністю, на які ви натякаєтеся, це здається, що в самому класі триває кілька етапів потоку / трансформації даних, що може свідчити про те, що клас руйнується за принципом єдиної відповідальності.

підозрюєте, що цей код був натхненний порадою тримати методи короткими (<= 5 рядків коду), уникати великих списків параметрів (<3 параметри) і що конструктори не повинні виконувати роботу (наприклад, виконуючи обчислення якогось такого типу, має істотне значення для об єкта).

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

На моєму досвіді, зробити заняття та методи невеликими є рідко, що я маю мати на увазі як окремий розгляд, швидше, це випливає дещо природним чином з однієї відповідальності.

Проте я б запропонував повністю змінити код, щоб клас став блакитним друком на фактичний об'єкт, а не на низку методів, які слід викликати (процедурно) у визначеному порядку.

Ви, мабуть, правильно зробите це. Немає нічого поганого в написанні прямого процесуального кодексу; є проблема зловживання парадигмою ОО для написання прихованого процесуального кодексу.

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

Зазвичай вам не слід вводити значення в поле як спосіб передачі його від одного методу до іншого; значення в полі повинно бути значущою частиною стану об'єкта в даний момент часу. (Я можу придумати деякі дійсні винятки, але не ті, де такі методи є загальнодоступними або де існує залежність порядку, про який повинен знати користувач класу)


2
Оголошення, оскільки: 1. Підкреслюючи СРП. "... малі методи ... дотримуйтесь природного" 2. Мета конструктора - дійсний стан. 3. "Дотримуйтесь деяких вказівок кодування, ігноруючи інші." Це кодируючі мертві кодування.
radarbob

6

Ви, швидше за все, тут переходите на неправильну схему. Невеликі функції та низький ступінь тяжкості рідко є проблемами самостійно. Справжньою проблемою тут є сполучення, яке викликає залежність порядку між функціями, тому шукайте способи вирішити це, не відкидаючи переваги малих функцій.

Код говорить голосніше, ніж слова. Люди отримують подібні виправлення набагато краще, якщо ви дійсно можете виконати частину рефакторингу та показати покращення, можливо, як вправу програмування пари. Коли я роблю це, мені часто буває складніше, ніж я думав, щоб правильно розробити дизайн, збалансувавши всі критерії.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.