Quantcast
Channel: Найцікавіше на DOU
Viewing all articles
Browse latest Browse all 8111

Що таке якісний дизайн. Відкритість, адитивність, відстежуваність

$
0
0


[Перевод на русский язык — в конце материала.]

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

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

Відкритість

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

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

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

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

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

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

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

Чудово. Ми зменшили кількість коду, але тепер програмістам перед його використанням потрібно обов’язково вивчати ці конвенції — і це вже фреймворк. А хотілося лише винести завантаження у загальний модуль, не займаючись системою конфігурації.

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

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

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

Адитивність

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

Засоби досягнення адитивності — відсутність переліку зв’язків між компонентами різних підсистем та слабка зв’язність. Нагадаємо: компоненти А і В називаються слабко пов’язаними, якщо для взаємодії їм не потрібно знати деталі реалізації одне одного. Існують дві основні техніки досягнення слабкої зв’язності. Перша — це взаємодія через загальний інтерфейс, друга — використання проксі об’єктів представників (тобто з боку А є один об’єкт, що інкапсулює взаємодію з В, аналогічно з боку В є представник А). Тоді для того, шоб змінити компоненту, досить переписати лише проксі представників, залишаючи все інше незмінним.

Чи є техніки розробки, що сприяють адитивності? Так. Перш за все, це розробка невеликими кроками. Тобто, якщо нам потрібно в одній зміні виконати задачі X, Y та Z, то давайте зробимо це послідовно (можливо, навіть розбивши їх на дрібніші частини) і для кожного випадку налаштуємо тести і окремий комміт. Дві маленькі проблеми вирішити простіше, ніж одну велику, до того ж, часта робота з кодом «шліфує» його, робить якіснішим із кожним переглядом. Тому мене дещо дивує бажання колег писати «ідеальний код»: краще прагнути писати код, який легко можна зрозуміти і модифікувати — тоді у нього будуть шанси за якийсь час стати ідеальним.

Відстежуваність (traceability)

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

Існують дві основні стратегії налагодження. Перша — це хаотичне генерування та перевірка конкретних гіпотез, друга (правильна) — процес локалізації, що має гарантовано зійтися, схожий на пошук лева у пустелі за допомогою ділення навпіл. Відстежуваність створюється шляхом систематичної підтримки другого способу.

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

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

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

Перевод на русский язык



Viewing all articles
Browse latest Browse all 8111

Trending Articles