Як управляються великими базами коду, що не є OO?


27

Я завжди бачу, що абстракція є дуже корисною функцією, яку забезпечує ОО для управління базою кодів. Але як керуються великими базами коду, що не є ООС? Або з часом вони просто стають " Великим балом грязі "?

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

За допомогою C можна дуже просто написати псевдо-OO-код. Я мало знаю про інші мови, що не належать до ООС. Отже, чи це спосіб управління великими базами кодів С?


6
Мовно агностично опишіть, будь ласка, об'єкт. Що це таке, як воно модифіковане, що має успадковувати і що воно повинно надавати? Ядро Linux наповнене виділеними структурами з великою кількістю помічників та покажчиків функцій, але це, мабуть, не задовольнить визначення більшості орієнтованих на об'єкт. Тим не менше, це один з найкращих прикладів дуже доглянутої кодової бази. Чому? Тому що кожен обслуговувач підсистеми знає, що знаходиться в зоні їх відповідальності.
Tim Post

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

@Tim Post Мене цікавить управління вихідним кодом ядра Linux. Скажіть, будь ласка, докладніше систему? Можливо, як відповідь із прикладом?
Гульшан

7
За старих часів ми використовували окремі сполучні елементи для макетів та заглушок для тестування одиниць. Ін'єкційна залежність - це лише одна методика серед кількох. Умовне складання - інше.
Macneil

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

Відповіді:


43

Ви, здається, думаєте, що ООП - єдиний засіб досягнення абстракції.

Хоча OOP, безумовно, дуже добре робить це, це аж ніяк не єдиний спосіб. Великі проекти також можуть бути керованими безкомпромісною модулярізацією (просто подивіться на Perl чи Python, обидва з них успішно в цьому, і так функціональні мови, як ML та Haskell), а також за допомогою механізмів, таких як шаблони (в C ++).


27
+1 Також можна створити "Велику кульку грязі" за допомогою OOP, якщо ви не знаєте, що робите.
Ларрі Коулман

Що з базовими кодами C?
Гульшан

6
@Gulshan: Багато великих баз кодів C є OOP. Тільки тому, що у C немає класів, це не означає, що OOP не можна досягти з невеликими зусиллями. Крім того, C дозволяє добре модулярізувати, використовуючи заголовки та ідіому PIMPL. Не майже настільки зручні чи потужні, як модулі в сучасних мовах, але ще раз досить добрі.
Конрад Рудольф

9
C дозволяє здійснювати модуляцію на рівні файлу. Інтерфейс знаходиться у файлі .h, загальнодоступні функції у файлі .c, а до приватних змінних та функцій staticдодається модифікатор доступу.
Девід Торнлі

1
@Konrad: хоча я погоджуюся, що OOP - це не єдиний спосіб зробити це, я вважаю, що ОП, мабуть, мав на увазі суто С, що не є ні функціональною, ні динамічною мовою. Тож я сумніваюся, що згадка про Перла та Хаскелла йому буде корисною. Я фактично вважаю ваш коментар більш актуальним і корисним для ОП ( не означає, що OOP не можна досягти з невеликими зусиллями ); ви можете розглянути можливість додавання його як окрему відповідь із додатковими подробицями, можливо, підтримується фрагментом коду або парою посилань. Це принаймні виграє мій голос, і цілком можливо, ОП. :)
Groo

11

Модулі, (зовнішні / внутрішні) функції, підпрограми ...

як сказав Конрад, OOP - не єдиний спосіб управління великими базами коду. Власне кажучи, перед цим було написано досить багато програмного забезпечення (раніше C ++ *).


* І так, я знаю, що C ++ не є єдиним, що підтримує OOP, але якимось чином тоді цей підхід почав сприйматися за інерцією.
Грак


6

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

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

Щоб почати рухатися до якогось автоматизованого тестування з існуючою базою коду, я рекомендую прочитати « Майстер ефективного роботи Майкла Пера» із Legacy Code , в якому детально описані підходи для зведення існуючих баз коду до тих пір, поки вони не повторюються. Це призводить до подібних ідей, на які відповіли інші, такі як модуляризація, але книга описує правильний підхід до цього, не порушуючи речі.


+1 для книги Майкла Пера. Коли ви відчуваєте депресію з приводу великої потворної бази коду, (перечитайте) - читайте :)
Matthieu

5

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

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

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


3

Насправді, як ви нещодавно виявили , функції першого порядку - це все, що вам потрібно для інверсії залежності.

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

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


2

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

У малих та середніх проектах це насправді простіше зробити з пуристською реалізацією ОО іноді.


2

Абстракція, абстрактні класи, введення залежності, інкапсуляція, інтерфейси тощо - не єдиний спосіб управління великими базами коду; це справедливий і об'єктно-орієнтований спосіб.

Основний секрет - уникати думки про OOP при кодуванні OOP.

Модульність є ключовою в мовах, що не належать до ОО. У C це досягається так само, як Девід Торнлі щойно згадував у коментарі:

Інтерфейс знаходиться у файлі .h, загальнодоступні функції у файлі .c, а до приватних змінних та функцій додається модифікатор статичного доступу.


1

Один із способів управління кодом - це його розкласти на наступні типи коду, уздовж ліній архітектури MVC (модель-перегляд-контролер).

  • Обробники вводу - Цей код стосується пристроїв введення, таких як миша, клавіатура, мережевий порт або абстракції вищого рівня, такі як системні події.
  • Обробники вихідних даних - Цей код стосується використання даних для маніпулювання зовнішніми пристроями, такими як монітори, світильники, мережеві порти тощо.
  • Моделі - Цей код стосується декларування структури постійних даних, правил перевірки стійких даних та збереження постійних даних на диску (або іншому постійному пристрої даних).
  • Перегляди - Цей код стосується форматування даних для задоволення вимог різних методів перегляду, таких як веб-браузери (HTML / CSS), графічний інтерфейс, командний рядок, формати даних протоколів зв'язку (наприклад, JSON, XML, ASN.1 тощо).
  • Алгоритми - Цей код повторно перетворює набір вхідних даних максимально швидко.
  • Контролери - Цей код приймає входи через обробники входів, аналізує входи за допомогою алгоритмів, а потім перетворює дані за допомогою інших алгоритмів, необов'язково поєднуючи входи з постійними даними або просто перетворюючи входи, а потім, можливо, зберігаючи перетворені дані в стійких за допомогою моделі програмне забезпечення та, можливо, перетворення даних за допомогою програмного забезпечення перегляду для візуалізації на вихідний пристрій.

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

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

Один із способів програмування OO ускладнює організацію коду тим, що деякі класи глибоко пов'язані зі стійкими структурами даних, а деякі - ні. Якщо стійкі структури даних тісно пов'язані з такими речами, як каскадні відносини 1: N або m: n відносини, дуже важко визначити межі класу, поки ви не зашифрували значну і значущу частину вашої системи, перш ніж ви дізнаєтесь, що ви правильно це зробили. . Будь-який клас, прив’язаний до стійких структур даних, буде важко еволюціонувати, коли схема постійних даних буде змінена. Класи, які обробляють алгоритми, форматування та синтаксичний аналіз, рідше можуть бути вразливими до змін у схемі стійких структур даних. Використання MVC типу організації коду краще ізолює найсильніші зміни коду до модельного коду.


0

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

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

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

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

Ми в основному знайомі з класами та методами рефакторингу, але з великою базою кодів такою мовою саме організаційна стратегія (у комплекті з документацією) потребує реконструкції, як і коли це необхідно.

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

Застереження також однакові: використовуйте тест регресії, переконайтеся, що ви можете легко відновити, якщо рефакторинг піде не так, і спроектуйте так, щоб полегшити рефакторинг в першу чергу (або ви просто цього не зробите!).

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


0

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


0

Як це обробляється, ви дізнаєтесь межі елементів, які ви використовуєте. Наприклад, наступні елементи в C ++ мають чітку межу, і будь-які залежності поза кордоном повинні бути ретельно продумані:

  1. вільна функція
  2. членська функція
  3. клас
  4. об’єкт
  5. інтерфейс
  6. вираз
  7. виклик / створення об'єктів конструктора
  8. виклик функції
  9. тип параметра шаблону

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

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


-1

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


-2

Emacs є хорошим прикладом цього:

Emacs Архітектура

Компоненти Emacs

У тестах Emacs Lisp використовуються skip-unlessі let-bindвиконуються функції виявлення та тестування приладів:

Іноді не має сенсу запускати тест через відсутність передумов. Можливо, необхідна функція Emacs не може бути скомпільована, функція, яка тестується, може викликати зовнішній бінарний файл, який може бути недоступний на тестовій машині, ви його називаєте. У цьому випадку макрос skip-unlessможна використовувати для пропуску тесту:

 (ert-deftest test-dbus ()
   "A test that checks D-BUS functionality."
   (skip-unless (featurep 'dbusbind))
   ...)

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

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

Як і SQLite. Ось його дизайн:

  1. sqlite3_open () → Відкрити підключення до нової або існуючої бази даних SQLite. Конструктор для sqlite3.

  2. sqlite3 → Об'єкт підключення до бази даних. Створено sqlite3_open () та знищено sqlite3_close ().

  3. sqlite3_stmt → Підготовлений об'єкт заяви. Створено sqlite3_prepare () та знищено sqlite3_finalize ().

  4. sqlite3_prepare () → Компілюйте текст SQL у байт-код, який буде виконувати роботу запиту чи оновлення бази даних. Конструктор для sqlite3_stmt.

  5. sqlite3_bind () → Зберігати дані програми в параметри вихідного SQL.

  6. sqlite3_step () → Перемістіть sqlite3_stmt до наступного рядка результатів або до завершення.

  7. sqlite3_column () → Значення стовпців у поточному рядку результатів для sqlite3_stmt.

  8. sqlite3_finalize () → Деструктор для sqlite3_stmt.

  9. sqlite3_exec () → Функція обгортки, яка виконує sqlite3_prepare (), sqlite3_step (), sqlite3_column () та sqlite3_finalize () для рядка одного або декількох операторів SQL.

  10. sqlite3_close () → Деструктор для sqlite3.

sqlite3 архітектури

Компоненти Tokenizer, Parser та Code Generator використовуються для обробки операторів SQL та перетворення їх у виконувані програми на мові віртуальної машини або в байт-код. Грубо кажучи, ці три верхні шари реалізують sqlite3_prepare_v2 () . Код байту, згенерований трьома верхніми шарами, є підготовленим оператором. Модуль Virtual Machine відповідає за запуск байтового коду оператора SQL. Модуль B-Tree організовує файл бази даних у кілька сховищ ключів / значень із упорядкованими ключами та логарифмічною продуктивністю. Модуль Пейджера відповідає за завантаження сторінок файлу бази даних у пам'ять, впровадження та контроль транзакцій, а також за створення та підтримку файлів журналів, які запобігають пошкодженню бази даних після збоїв або відключення живлення. Інтерфейс ОС - це тонка абстракція, яка забезпечує загальний набір процедур адаптації SQLite до роботи в різних операційних системах. Грубо кажучи, нижній чотири шари реалізують sqlite3_step () .

віртуальна таблиця sqlite3

Віртуальна таблиця - це об'єкт, який реєструється у відкритому підключенні до бази даних SQLite. З точки зору оператора SQL, об'єкт віртуальної таблиці виглядає як будь-яка інша таблиця або подання. Але поза кадром, запити та оновлення у віртуальній таблиці викликають методи зворотного виклику об'єкта віртуальної таблиці замість читання та запису у файл бази даних.

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

Ось кілька існуючих та постульованих застосувань для віртуальних таблиць:

Повнотекстовий інтерфейс пошуку
Просторові індекси за допомогою R-Дерев
Огляд дискового вмісту файлу бази даних SQLite (віртуальна таблиця dbstat)
Прочитайте та / або запишіть вміст файлу значень, розділених комами (CSV)
Доступ до файлової системи хост-комп'ютера так, ніби це таблиця бази даних
Включення SQL-маніпулювання даними в статистичних пакетах типу R

SQLite використовує різні методи тестування, включаючи:

Три незалежно розроблені тестові джгути
100% тест покриття гілки в конфігурації під час розгортання
Мільйони і мільйони тестових випадків
Тести поза пам'яттю
Тести на помилки вводу / виводу
Випробування на аварію та втрати потужності
Тести Фьюз
Тести граничного значення
Тести на оптимізацію для відключених
Регресійні тести
Неправильно сформовані тести бази даних
Широке використання перевірок assrt () та часу виконання
Вальгринд-аналіз
Невизначені перевірки поведінки
Контрольні списки

Список літератури

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