Ви визначили дійсну угоду
Отже, тут справді є компроміс, і це властиво абстракції в цілому. Кожен раз, коли хтось намагається витягнути N рядків коду у свою функцію для того, щоб назвати його та виділити, вони одночасно полегшують читання сайту, що викликає (посилаючись на ім’я, а не на всі деталі горі, що лежать в основі цього імені) та більш складний (тепер у вас є сенс, який заплутаний у двох різних частинах кодової бази). "Легко" - це протилежність "важкому", але це не синонім "простий", який є протилежнім "складного". Два не є протилежністю, і абстракція завжди збільшує складність для того, щоб вставити ту чи іншу форму легкості.
Ми можемо побачити додаткову складність безпосередньо, коли деяка зміна вимог бізнесу змушує абстракцію почати просочуватися. Можливо, якась нова логіка пішла б найбільш природно посеред попередньо абстрагованого коду, скажімо, наприклад, якщо абстрагований код перетинає якесь дерево, і ви дійсно хотіли б зібрати (і, можливо, діяти) якусь інформацію, поки ви обхід дерева. Тим часом, якщо ви абстрагували цей код, то можуть бути й інші сайти викликів, і додавання необхідної логіки в середину методу може порушити ці інші сайти викликів. Дивіться, щоразу, коли ми змінюємо рядок коду, нам потрібно лише переглянути безпосередній контекст цього рядка коду; коли ми змінюємо метод, ми мусимо Cmd-F весь наш вихідний код шукати все, що може зламатися в результаті зміни контракту цього методу,
Жадібний алгоритм може вийти з ладу в цих випадках
Складність також зробила код у певному сенсі менш читабельним, ніж більше. У попередньому завданні я розглядав HTTP API, який був дуже ретельно та точно структурований у кілька шарів, кожна кінцева точка визначається контролером, який перевіряє форму вхідного повідомлення та передає його якомусь менеджеру "бізнес-логічного рівня" , який потім зробив деякий запит деякого "рівня даних", який відповідав за внесення декількох запитів до деякого рівня "об'єкта доступу до даних", який відповідав за створення декількох делегатів SQL, які насправді відповіли б на ваше запитання. Перше, що я можу сказати про це: щось на зразок 90% коду було копіювально-вставним котлом, іншими словами, це не було. Тож у багатьох випадках читання будь-якого проходу коду було дуже "простим", оскільки "о, цей менеджер просто пересилає запит до цього об'єкта доступу до даних".багато контекстної комутації та пошуку файлів і намагаються відслідковувати інформацію, яку ви ніколи не мали б відслідковувати, "це називається X на цьому шарі, воно називається X" на цьому іншому шарі, тоді воно називається X '"в цьому іншому інший шар. "
Я думаю, що коли я припинив, цей простий API CRUD був на етапі, коли, якщо ви надрукували його по 30 рядків на сторінку, це займе 10-20 підручників з п'ятисот сторінок на полиці: це була ціла повторювана енциклопедія код. Щодо суттєвої складності, я не впевнений, що там було навіть половина підручника суттєвої складності; У нас було лише 5-6 діаграм баз даних, щоб обробити це. Внести будь-які незначні зміни до цього, це було завдання мамонта, навчившись це мамонта, додавання нової функціональності стало настільки болючим, що ми насправді мали файли шаблонів шаблонів, які ми використали для додання нової функціональності.
Тож я з перших рук бачив, як зробити кожну частину дуже читабельною та очевидною може зробити цілу дуже нечитабельною та неочевидною. Це означає, що жадібний алгоритм може вийти з ладу. Ви знаєте жадібний алгоритм, так? "Я зроблю будь-який крок, що локально покращує ситуацію найбільше, і тоді я буду довіряти, що опинюсь у глобально покращеній ситуації". Це часто красива перша спроба, але вона також може пропустити складний контекст. Наприклад, у виробництві ви можете спробувати підвищити ефективність кожного конкретного кроку у складному виробничому процесі - робіть великі партії, кричите на людей, які, здається, нічого не роблять, щоб зайнятись чим-небудь іншим - і це часто може знищити глобальну ефективність системи.
Найкраща практика: використовуйте DRY та довжину для здійснення дзвінка
(Примітка. Цей заголовок розділу дещо жартує; я часто кажу своїм друзям, що коли хтось каже "ми повинні робити X, оскільки найкращі практики говорять так ", вони 90% часу не говорять про щось на зразок ін'єкції SQL або хешування паролів або що завгодно - односторонні найкращі практики - і тому заява може бути переведена за те, що 90% часу "ми повинні робити X, бо я так кажу ". Начебто у них може бути стаття в блозі від якогось бізнесу, який зробив кращу роботу з X, а не з X, але, як правило, немає гарантії, що ваш бізнес схожий на цей бізнес, і, як правило, є якась інша стаття з іншого бізнесу, яка зробила кращу роботу з X ', а не X. Тому, будь ласка, не приймайте заголовок теж серйозно.)
Те, що я рекомендував би, ґрунтується на розмові Джека Дідеріха під назвою « Написати класи» (youtube.com) . У цій розмові він зазначає кілька чудових моментів: наприклад, те, що ви можете знати, що клас насправді є лише функцією, коли він має лише два загальнодоступних методу, і один з них - конструктор / ініціалізатор. Але в одному випадку він говорить про те, як гіпотетична бібліотека, яку він замінив на рядок для розмови, як "Muffin" оголосила власний клас "MuffinHash", який був підкласом вбудованого dict
типу, який має Python. Реалізація була абсолютно порожньою - хтось тільки подумав: "Можливо, нам доведеться пізніше додати спеціальну функціональність до словників Python; давайте введемо абстракцію прямо на всякий випадок".
І його заперечна відповідь була просто: "ми завжди можемо це зробити пізніше, якщо нам потрібно".
Я думаю, що ми іноді робимо вигляд, що ми будемо гіршими програмістами в майбутньому, ніж зараз, тому ми можемо захотіти вставити якусь дрібницю, яка може зробити нас щасливими в майбутньому. Ми передбачаємо потреби майбутнього. "Якщо трафік в 100 разів більший, ніж ми думаємо, буде, такий підхід не буде масштабуватися, тому нам доведеться внести передові інвестиції в цей більш жорсткий підхід, який буде масштабуватися". Дуже підозріло.
Якщо ми сприймемо цю пораду серйозно, тоді нам потрібно визначити, коли настало “пізніше”. Напевно, найбільш очевидним було б встановлення верхньої межі довжини речей з міркувань стилю. І я думаю, що найкращою порадою було б використовувати DRY - не повторювати себе - з цими евристиками про довжину ліній, щоб закріпити дірку в принципах SOLID. На основі евристики з 30 рядків, що є "сторінкою" тексту та аналогією з прозою,
- Refactor перевірка функції / методу, коли ви хочете скопіювати її та вставити. Можливо, є випадкові поважні причини копіювання та вставки, але ви завжди повинні відчувати себе брудно. Справжні автори не змушують вас читати велике довге речення 50 разів протягом усієї розповіді, якщо вони справді не намагаються висвітлити тему.
- Функція / метод в ідеалі має бути "абзацом". Більшість функцій мають становити близько половини сторінки або 1–15 рядків коду, і лише, можливо, 10% ваших функцій повинні бути дозволені до діапазону до половини сторінки, 45 рядків і більше. Отримавши 120+ рядків коду та коментарів, цю річ потрібно розбити на частини.
- Файл в ідеалі повинен бути "главою". Більшість файлів мають бути довжиною 12 сторінок або менше, тому 360 рядків коду та коментарів. Лише, можливо, 10% ваших файлів повинні мати доступ до 50 сторінок або 1500 рядків коду та коментарів.
- В ідеалі більшість вашого коду має бути з відступом базовою лінією функції або глибиною на один рівень. Виходячи з деяких евристик щодо вихідного дерева Linux, якщо ви релігійні щодо цього, лише, можливо, 10% вашого коду має бути з відступом 2 рівня або більше в межах базової лінії, менше 5% з відступом 3 рівня або більше. Це означає, зокрема, що речі, які потребують "обговорення" якоїсь іншої проблеми, як, наприклад, обробка помилок під час великої спроби / лову, повинні бути виведені з фактичної логіки.
Як я вже згадував там, я перевіряв ці статистичні дані на поточному дереві джерела Linux, щоб знайти ці приблизні відсотки, але вони також є певними причинами в літературній аналогії.