Чи повинні абстракції зменшити читабельність коду?


19

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

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

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

Моє запитання: коли рамка або модель вводить стільки накладних витрат, як це, чи варто цього? Це симптом погано реалізованої схеми?

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

Відповіді:


17

Це дійсно більше тривалий коментар до відповіді @kevin cline.

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

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

На противагу цьому, коли ви натрапляєте на нього в Java, ви набагато більше шансів побачити якийсь варіант добре відомого "корпоративного привіт світу", де замість одного тривіального класу, який робить щось просте, ви отримуєте абстрактний базовий клас і конкретний похідний клас, який реалізує інтерфейс X, і створюється заводським класом в рамках DI тощо. 10 рядків коду, які виконують справжню роботу, перебувають під 5000 лініями інфраструктури.

Частина цього залежить як мінімум настільки, наскільки мова - робота безпосередньо з віконними середовищами, такими як X11 та MS Windows, є горезвісною для того, щоб перетворити тривіальну програму "привіт світ" на 300+ ліній майже нерозбірливого сміття. З часом ми розробили різні набори інструментів для того, щоб уникнути нас від цього - але 1) ці набори інструментів самі по собі є нетривіальними, і 2) кінцевий результат все ще не тільки більший і складніший, але і, як правило, менш гнучкий. ніж текстовий еквівалент (наприклад, незважаючи на те, що він просто роздруковує текст, перенаправлення його до файлу рідко можливо / підтримується).

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


7

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


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

3

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

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

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

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

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


3
Ну, недостатньо абстракції, і ваш код важко зрозуміти, тому що ви не можете виділити, які частини роблять що. Це капсулювання, а не абстракція. Ви можете ізолювати деталі в бетонних класах без особливої ​​абстракції.
Заява

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

1
@Statement: Інкапсуляція даних - це, звичайно, абстракція.
Ед С.

Хоча ієрархії простору імен дуже приємні.
JAB

2

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

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

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

Занадто багато речей забезпечують функціональність

Альтернативою раніше, коли я працював у попередніх кодових базах, була система з сотнями до тисяч в основному крихітних об'єктів на відміну від кількох десятків об'ємних систем, де деякі об'єкти використовувались просто для передачі повідомлень від одного об’єкта до іншого ( Messageоб'єкта, наприклад, який мав свою власний публічний інтерфейс). Це, головним чином, те, що ви отримуєте аналогічно, повертаючи ECS назад до точки, де компоненти мають функціональні можливості, і кожна унікальна комбінація компонентів в об'єкті дає свій власний тип об'єкта. І це, як правило, дає менші, простіші функції, успадковані та надані нескінченними комбінаціями об'єктів, що моделюють маленькі ідеї ( Particleobject vs.Physics System, наприклад). Однак він також має тенденцію отримати складний графік взаємозалежностей, що ускладнює міркування про те, що відбувається з широкого рівня, просто тому, що в кодовій базі є так багато речей, які насправді можуть щось зробити, а тому можуть зробити щось не так - - типи, які не є типом "даних", а "об'єктними" типами з пов'язаною функціональністю. Типи, які служать чистими даними без пов’язаних функціональних можливостей, не можуть зійти з ладу, оскільки нічого не можуть зробити самостійно.

Чисті інтерфейси не дуже допомагають цій проблемі зрозумілості, тому що навіть якщо це робить "залежність від часу компіляції" менш складною та надає більше місця для дихання для змін та розширення, це не робить "залежності часу виконання" та взаємодії менш складними. Клієнтський об’єкт все ще закінчує викликом функцій конкретного об'єкта акаунта, навіть якщо вони викликаються через IAccount. Поліморфізм та абстрактні інтерфейси мають свою користь, але вони не розв'язують речі таким чином, що справді допомагає вам обґрунтувати всі побічні ефекти, які можуть тривати в будь-який момент. Щоб досягти цього типу ефективної розв'язки, вам потрібна база коду, яка має набагато менше речей, що містять функціональні можливості.

Більше даних, менша функціональність

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

30 простих речей не обов’язково простіше міркувати про більш ніж 1 складнішу річ, якщо ці 30 простіших речей взаємопов'язані, тоді як складна річ стоїть сама по собі. Отже, моя пропозиція насправді перенести складність від взаємодії між об'єктами і більше на більш об'ємні об'єкти, які не повинні взаємодіяти ні з чим іншим, щоб досягти масової розв'язки, на цілі "системи" (не моноліти та об'єкти бога, пам'ятайте, і не класи з 200 методами, але щось значно вищий рівень, ніж a Messageчи a, Particleнезважаючи на мінімалістичний інтерфейс). І віддайте перевагу більш простим старим типам даних. Чим більше ви залежите від них, тим менше зчеплення ви отримаєте. Навіть якщо це суперечить деяким ідеям SE, я виявив, що це дуже багато допомагає.


0

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

Можливо, це симптом вибору неправильної мови програмування.


1
Я не бачу, як це має щось спільне з мовою вибору. Абстракції - це поняття, незалежне від мови на високому рівні.
Ред С.

@Ed: Деякі абстракції в деяких мовах більш просто реалізовані, ніж в інших.
Кевін Клайн

Так, але це не означає, що ви не зможете написати ідеально ретельною і легко зрозумілою абстракцією цими мовами. Моя думка полягала в тому, що ваша відповідь жодним чином не відповідає на питання чи не допомагає ОП.
Ред С.

0

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


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