Не забудьте поширити ❤️
Якщо говорити про абревіатури, в індустрії розробки програм ви їх зустрінете у великих кількостях. Тут KISS (англ. «Поцілунок»), там SLAP (англ. «Ляпас») – назви інтригуючі, але якісь безглузді. Якщо ви тільки починаєте вивчати програмування, подібні слівця, що прослизають в поясненнях, можуть здорово дратувати. Особливо, якщо ви не знаєте, що вони означають! Якщо це якраз ваш випадок, цей пост – для вас!
У цій статті ми розберемо різні принципи програмування з дивними назвами-абревіатурами. Деякі з них широко відомі, інші зустрічаються в текстах рідше. Їх складність для розуміння і застосування теж варіюється. Тому я постараюся детальніше пояснити теорію, що стоїть за кожним з цих принципів. А найцікавішу частину – їх реалізацію – я залишаю вам.
KISS
Почнемо з найпопулярніших. Keep It Stupid Simple («Дотримуйся простоти», абревіатура KISS як окреме слова означає «поцілунок») це один з найвідоміших принципів програмування. Значення його досить зрозуміле, хоча і дуже широке.
Як не складно здогадатися, цей принцип велить вам стежити за тим, щоб код залишався якомога простішим. Чим код простіший, тим легше в ньому розібратися, як вам, так і іншим людям, які займаються його підтримкою. Під простотою головним чином мається на увазі відмова від використання хитромудрих прийомів і непотрібного ускладнення.
Як приклади порушення цього принципу можна назвати написання окремої функції тільки для здійснення операції додавання або використання побітового оператора (right shift >> 1) для розподілу цілих чисел на 2. Останнє, безумовно, ефективніше, ніж звичайне (/ 2), але при цьому дуже сильно знижується зрозумілість коду. Застосовуючи такий підхід, ви здійснюєте clever coding («заумний» кодинг») і over-optimization (надмірну оптимізацію). І те, і інше в довгостроковій перспективі не дуже добре позначається на здоров’ї вашого коду.
DRY
Принцип Do not Repeat Yourself («Не повторюйте», абревіатура DRY як окреме слово означає «сухий») за своєю природою дуже схожий на принцип KISS. Він теж має досить просте, але широке значення.
Багатьом розробникам трапляється робити копіпаст і дублювання фрагментів власного коду. Загалом, в цьому немає нічого поганого. У всіх часом виникає необхідність швидко перевірити що-небудь (очікувану поведінку або щось інше), щоб визначити, чи варто це писати (тобто писати правильно). А ось випуск такого скопійованого коду в виробництво неприйнятний.
DRY нагадує нам, що кожну повторювану поведінку в коді слід відокремлювати (наприклад, виділяти в окрему функцію) для можливості багаторазового використання. Коли у вас в кодовій базі є два абсолютно однакові фрагменти коду, це не добре. Це часто призводить до розсинхронізації та інших багів, не кажучи вже про те, що від цього збільшується розмір програми.
YAGNI
YAGNI це безперечно найдовша абревіатура в нашому списку. Принцип You Are not Gonna Need It («Тобі це не знадобиться», YAGNI) може суперечити точці зору деяких програмістів.
Готовність до майбутнього зазвичай вважається справою хорошою, але не в програмуванні. Залишати будь-який код, призначений тільки для розширюваності програми в майбутньому, це неправильно. Але якщо це суперечить вашим переконанням, давайте розберемо докладніше.
Проєкти програмного забезпечення не мають чіткого кінця. Якщо тільки творець проєкту не закидає свою ідею взагалі (і при цьому навіть не передає її комусь ще), проеєкт, по суті, постійно розвивається. Але при цьому немає ніякої точки, де можна було б визнати проєкт «досить хорошим» і зупинитися. Завжди буде можливість щось поліпшити.
Намагатися спрогнозувати майбутнє і уявляти, як повинен виглядати ваш код, це не погано. Але небажано залишати в продакшені «точки розширення» (місця, призначені тільки для того, щоб дозволити вам в майбутньому легко додати новий функціонал). Звичайно, ми не говоримо про випадки, коли мова йде про вже замовлений функціонал. Такі точки розширення вносять непотрібну складність і збільшують розмір вашої кодової бази. Якщо задуматися, це також суперечить описаному вище принципу KISS.
SLAP
Тільки уявіть: код можна не тільки поцілувати (KISS) і висушити (DRY), але також і шльопнути (SLAP)!
Single Level of Abstraction Principle («Принцип єдиного рівня абстракцій», SLAP) диктує нам, як ми повинні організовувати свій код (зокрема, функції), щоб він залишався підтримуваним.
З довгими і складними функціями важко ужитися. У них складно розібратися непосвяченій людині, їх важко тестувати і, найчастіше, навіть щоб їх побачити повністю, доводиться користуватися прокруткою. Якщо у вас виникають подібні проблеми, слід негайно замислитися про реструктуризацію функції і створення замість неї декількох функцій поменше. Пам’ятайте, що
«Функції повинні виконувати тільки одну дію, але виконувати її добре» (Роберт Мартін).
Але як саме слід організовувати ці дрібніші функції? Яку саме «одну дію» повинна виконувати кожна з них? Ну, коли ви придбаєте більше досвіду в програмуванні, ви почнете відчувати, де і що слід розташовувати, а SLAP допоможе вам в цьому.
Ваші функції повинні робити щось одне або, якщо застосовувати принцип SLAP, вони повинні мати єдиний рівень абстракції. Скажімо, функція, що читає input, не повинна також обробляти отримані дані. Для цього вона повинна задіяти окрему функцію, що знаходиться на іншому, нижчому рівні абстракції. Чим більш загальною є функція і чим більше інших функцій вона використовує, тим вище вона розташовується в абстракційній ієрархії.
SRP
Single Responsibility Principle («Принцип єдиної відповідальності», SRP) в чомусь схожий на SLAP, але спрямований на об’єктно-орієнтоване програмування. Цей принцип говорить, що об’єкти і класи (а також функції та методи) потрібно організовувати так, щоб кожен з них мав тільки одну зону відповідальності.
Відповідальність об’єктів і класів легко організовувати, коли вони відображають більш «життєві» об’єкти. Але коли ми маємо справу з сутностями, що мають в назві слова «контролер» або «сервіс», ситуація ускладнюється. Ці високорівневі модулі важко організовувати, оскільки, теоретично, в них можна помістити що завгодно. При цьому число відповідальностей таких сутностей стрімко зростає, а в результаті весь код стає складнішим і незрозумілішим.
Як впоратися з цією проблемою? Скажімо, наш контролер відповідає за комп’ютер. Він повинен контролювати температуру CPU, швидкість вентилятора, дисковий простір, зовнішні пристрої тощо. Майте на увазі, що це означає не тільки властивості, але й методи, з якими ми співпрацюємо. Як щодо того, щоб створити кілька класів замість того, щоб зберігати всі безпосередньо в одному класі? Так у нас з’являються DevicesController, DiskSpaceController тощо. А після ми можемо використовувати всі ці класи, щоб скласти високорівневий клас Controller, який тепер буде куди простіше підтримувати. Звичайно, в реальності подібний код зажадає куди більшої організації, але, я сподіваюся, ідею ви вловили.
OCP
Ми вже говорили про розширюваність коду, коли обговорювали YAGNI. Open-Closed Principle («Принцип відкритості-закритості», OCP) деяким чином пов’язаний з попереднім правилом, але це ще один погляд на речі.
OCP вимагає, щоб код був відкритий для нових, майбутніх доповнень, і щоб при їх додаванні не доводилося змінювати вже написаний код. Цей принцип більшою мірою торкається питань архітектури, ніж коду як такого.
Виходить, OCP конфліктує з YAGNI? Адже це дві протилежні точки зору на майбутнє нашого коду! Ні, це не так.
YAGNI передбачає відмову від додавання коду, який не використовується в даний час. А OCP зачіпає глибші речі, саму архітектуру вашого коду. Дотримання принципу OCP не означає, що ви повинні писати даремний в даний час код, який може стати в нагоді в майбутньому. Йдеться про проєктування всієї кодової бази таким чином, щоб вона могла легко розширюватися.
Ви робите ваше «ядро» розширюваним, будуєте на його основі функціонал, необхідний в даний час, і разом з тим зберігаєте архітектуру, готову для внесення змін в майбутньому. Все це не передбачає написання мертвого коду.
LSP
Liskov Substitution Principle («Принцип підстановки Барбари Лісков», LSP) названий на честь його автора, Барбари Лісков. Це принцип об’єктно-орієнтованого програмування, що стосується класів, інтерфейсів, типів і підтипів.
Саме собою це правило досить просте і логічне, але спочатку його може бути важко застосовувати на практиці. Суть його в тому, що кожен підтип повинен доповнювати, а не замінювати базовий тип. Найпростіше зрозуміти це, розібравши приклад (зазвичай для ілюстрації принципу використовується проблема квадрата і прямокутника).
ISP
Interface Segregation Principle («Принцип поділу інтерфейсу», ISP) це ще один принцип, який стосується теми організації коду. Він головним чином стосується інтерфейсів і статично типізованих мов програмування. Тобто, люди, які пишуть код на JavaScript, не часто будуть стикатися із застосуванням цього принципу на практиці. Проте, знання цього принципу в будь-якому випадку сприяє поліпшенню коду.
Інтерфейси допомагають працювати швидше з формою даних, а не з даними як такими. Правильне їх написання і організація дають прекрасну можливість поліпшити підтримування вашого коду без втрат продуктивності.
Принцип ISP зачіпає саме цю сферу – використання інтерфейсів для відокремлення коду і одночасно організації самих інтерфейсів. Візьмемо, наприклад, спадкування класів. Можливо, вам не важливі певні методи або властивості базового класу і ви хотіли б їх «пропустити»? Простий інтерфейс може допомогти вам в цьому! У компільованих і статично типізованих мовах це також дає вам чистішу зону видимості і швидшу компіляцію (якщо зміняться властивості батьківського класу, які не стосуються даного інтерфейсу, підкласи не потрібно буде перекомпільовати).
DIP
Як і OCP, Dependency Inversion Principle («Принцип інверсії залежностей», DIP) більшою мірою стосується загальної архітектури вашого коду. Фактично, це один з найважливіших принципів проєктування архітектури коду.
Принцип DIP трохи важкуватий, але щоб його дотримуватися, потрібно засвоїти лише дві речі. По-перше, ваш код повинен бути написаний так, щоб деталі реалізації (наприклад, призначений для користувача інтерфейс або база даних) залежали від основної логіки (правил бізнесу), а не навпаки.
По-друге, всі ці залежності не повинні бути прямими. Їх потрібно абстрагувати за допомогою інтерфейсів, щоб ваша основна логіка працювала з усім, що б ви їй ні передали, вимагаючи для цього тільки який-небудь простий «міст».
SOLID
П’ять принципів, які ми вже обговорили – SRP, OCP, LSP, ISP, DIP – разом становлять набір принципів SOLID, описаний Робертом Мартіном. Ці принципи сприяють створенню гарного об’єктно-орієнтованого (і не тільки) коду.
Сподіваюся, я розкрив цю тему досить, щоб розробники-початківці познайомилися з основними принципами світу програмування. Але якщо хочете копнути глибше, варто пошукати матеріали в інтернеті. Я докладу лише кілька джерел:
- SOLID Principles: Explanation and examples
- SOLID Principles made easy
- S.O.L.I.D: The First 5 Principles of Object Oriented Design
Ми у соцмережах: