×Закрыть

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


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

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

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

Відкритість

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

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

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

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

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

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

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

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

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

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

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

Адитивність

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

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

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

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

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

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

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

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

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

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


11 комментариев

Подписаться на комментарииОтписаться от комментариев Комментарии могут оставлять только пользователи с подтвержденными аккаунтами.

Тема сисек не раскрыта! Это не статья, а хоку в виде набора слов авторской (недо)интерпретации, зерно которой кроется в следующем: реализация проекта должна осуществляться в виде постепенного наращивания функциональности, без предварительного продумывания архитектуры и абстрактных моделей, с минимальной зависимостью модулей друг от друга, и «чтобы можно было отследить, как все работает».

Начните лучше со статьи Что такое хорошая статья.

Проектирование программных систем все еще ближе к кустарному ремеслу или искусству, нежели к науке
В вашем конкретном случае — да! Но если хотите избежать данной участи начните с чего-то в стиле www-csd.univer.kharkov.ua/.../cat16/opps.pdf
В математической логике есть такое понятие как «открытый мир».
Уж, как бы, если решили порадовать знаниями логики, то писали бы про формальную логику, поскольку «открытый и закрытый миры» не являются объектами исследования математической логики. В этом же абзаце сопоставляете понятие «факт» с понятием «свойство». Логика такой неперманентности понятий для меня загадка!

Ну, и так далее по тексту... Как то непонятно почему небольшие структурные изменения библиотеки автоматически сделали библиотеку фреймворком.

В результате получаем чуть ли не двукратное увеличение сложности на ровном месте.
Метрику в студию. Ах, да, сложности чего? Сложность она такая сложная для оценки сложности, что сложно даже выделить критерии оценки этой самой сложности, и если это слишком сложно для вашего понимания степени сложности, то к чему все эти сложности? :)
Если язык разработки позволяет рефакторинг
А можно посмотреть список тех, которые не позволяют, а то может я не на том пишу :)
Средства достижения аддитивности — отсутствие точек перечислений связей между компонентами разных подсистем и слабая связность.
Какая слабая связность при полном отсутствии точек перечислений связей. Как контролировать степень связности при отсутствии этих самых точек? Сама логика высказывания «средством является отсутствие» вас не пугает?
Есть ли техники, повышающие аддитивность? Да. Это, прежде всего, разработка небольшими шагами.
Неправда! Имплементация маленькими или большими шагами никак не влияет на огрехи проектирования. Фраза «давайте делать по чуть-чуть» подходит любителям, и именно так вы предлагаете им перестать быть любителям :) Не кажется алогичным?

В целом аддитивность у вас раскрыта по Agile! Вы случайно не записались к ним на курсы? ;)

Первая — это хаотичное выдвижение и проверка конкретных гипотез, вторая (правильная) — некий гарантированно сходящийся процесс локализации, похожий на поиски льва в пустыне методом деления надвое. Трассируемость создается путем систематической поддержки второго способа.
Итак, делаем вывод: отладку необходимо проводить систематическим поиском льва в пустыне методом деления на два (неясно правда кого пустыни или львы и как в неупорядоченном пространстве этот метод вообще применим). У меня просто истерика и вынос мозга :)

П.С. Ну, и называть конвенциями описания автора недофреймворка путей корректного использования его библиотек — это было сильно !
П.С.П.С. А вообще думал статья о графическом дизайне :)

Фу блін, прочитав статтю — було ок, хоч і не без питань, прочитав цей коммент — наче лайна наївся.

Наступного разу пишіть свою критику автору в приват або залишайте при собі

Хороша стаття, дякую.

А на англійській не планується переклад? (Чи може є щось схоже).
Бо сильно хочеться «ткнути» декого носом )

Якщо прийняти за істину те, що «Відкритість ~ Visibility» і «Відстежуваність ~ Controllability», то можна посилатися на статтю Bret Pettichord «Design for Testability» ( goo.gl/ESfBRD ), а якій показано, як ці властивості коду корелюють з тим, наскільки він придатний до тестування.

Хорошая статья, но какие можно использовать мат. критерии для анализа кода? Например,, насколько адекватную оценку кода дает pmd.sourceforge.net/eclipse можно ли оценить количественно аддитивность и отслеживаемость автоматически?

Хороший вопрос. Такие тулзы могут дать локальную метрику и локальные дефекты — она может как-то вероятностно относиться к глобальным показателям (типа если много локальных дефектов определенного типа — то большая вероятность что програма в целом какая-то некакая) — но вобще переход от «локальных свойств» к «общему взгляду» это как раз то, что плохо формализуется. Некоторое исключение — трассируемость, (формализуется легко, но проверка упирается в алгоритмические переборные задачи с экспотенциальной сложностью.)

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

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

Хороша стаття

Подписаться на комментарии