Як уникнути нескінченного повторення через однаково неоптимальні конструкції?


10

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


Приклад:

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

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

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

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


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

Редагувати: Здається, є ряд тлумачень питання, які я хочу прояснити:

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

3
Не "підписуйтесь на філософію ОО". Це інструмент, а не головне життєве рішення. Використовуйте його, коли він допомагає, і не використовуйте його, коли цього не відбувається.
користувач253751

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

2
О, С має закономірності. Вони просто дуже різні візерунки. :)
candied_orange

Я прояснив більшість цих неправильних тлумачень у редакції
Джонатан,

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

Відповіді:


8

Коли я діходжу до такого жорсткого рішення, я зазвичай задаю собі три питання:

  1. Які плюси і мінуси всіх доступних рішень?

  2. Чи є рішення, я ще не розглядав?

  3. Найголовніше:
    які саме мої вимоги? Чи не вимоги до паперу, справжні фундаментальні?
    Чи можу я якось переформулювати проблему / змінити її вимоги, щоб вона могла мати просте, прямолінійне рішення?
    Чи можу я представити свої дані якимось різним чином, щоб це дозволило зробити таке просте, прямолінійне рішення?

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

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

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

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


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

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

ОП, я бачу, що ви застрягли в механіці рішення коду, так що так, ви «можете також позначити цю відповідь». Найкращий код, який ми написали, був після дуже ретельного аналізу вимог, похідних вимог, діаграм класів, взаємодії класів і т. Д. Слава богу, що ми протистояли всім обробкам з дешевих місць (читайте керівництво та колеги): "Ви "Ви витрачаєте занадто багато часу на дизайн", "поспішіть і отримайте кодування!", "Це занадто багато класів!" і т. д. Після того, як ми до цього потрапили, кодування стало радістю - більше, ніж розвагою. Об'єктивно це був найкращий код у всьому проекті.
radarbob

13

По-перше, спочатку - візерунки - корисні абстракції, а не кінець, все це дизайн, не кажучи вже про дизайн OO.

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

Тепер до м’яса речей:

Це стає дуже важким, коли, мабуть, кожен підхід має стільки ж проблем, як і будь-який з інших.

Чому? Коли у вас є купа подібних варіантів, ваше рішення повинно бути простішим! Ви не втратите багато, вибравши "неправильний" варіант. І дійсно, код не виправлений. Спробуйте щось, подивіться, чи добре це. Ітерація .

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

Жорсткі горіхи. Важкі проблеми - актуальні математично важкі проблеми просто важкі. Доведено важко. Для них буквально немає хорошого рішення. І виявляється, легкі проблеми насправді не цінні.

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

Це призводить до втрати мотивації, оскільки "чистий" код іноді перестає бути варіантом.

Досконалий - ворог добра. Отримайте щось працююче, а потім переробляйте його.

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

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


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

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

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

8

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

Це як спробувати створити машину, починаючи з шини.

Що вам потрібно - це зробити крок назад і подивитися на більшу картину. Як цей лист сидить у загальній конструкції? Це все ще актуально і правильно?

Як би виглядав модуль, якби ви спроектували та впровадили його з нуля? Наскільки далеко від цього «ідеалу» - ваша поточна реалізація.

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


4

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

Найкраща порада, яку я можу дати вам у цій ситуації:

  • не намагайтеся досягти своєї головної мети одним "великим ударом" ,

  • дізнайтеся, як покращити свій код меншими кроками!

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

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

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

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

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

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

  • Рефакторинг Фоулера: описує повний каталог дуже маленьких рефакторингах.

  • Ефективна робота зі застарілим кодом від Feathers: дає чудові поради, як поводитися з великими фрагментами погано розробленого коду та зробити його більш перевіреним за менші кроки


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

1

Іноді кращими двома принципами дизайну є KISS * та YAGNI **. Не відчувайте необхідності втиснути кожен відомий зразок дизайну в програму, яка просто повинна надрукувати "Привіт, світ!".

 * Keep It Simple, Stupid
 ** You Ain't Gonna Need It

Відредагуйте після оновлення запитання (і відображаючи певну міру того, що каже Пітер Б):

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

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


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

Ну добре, так, здається, існує консенсус щодо відступу та переоцінки проблеми. Це гарна ідея.
Джонатан

1

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

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

З вищезазначеним пов'язане проектування або налагодження за допомогою зізнання. Ти йдеш до колеги і кажеш: "Я скажу тобі проблему, яка у мене є, а потім я поясню тобі рішення, які є у мене. Ти вказуєш на проблеми в кожному підході і пропонуєш інші підходи . " Часто перед тим, як інша людина навіть заговорить, коли я пояснюю, я починаю розуміти, що один шлях, який здавався рівним іншим, насправді набагато кращий чи гірший, ніж я спочатку думав. В результаті розмова може або підсилити таке сприйняття, або вказати на нові речі, про які я не думав.

Тож TL; DR: Перервіться, не змушуйте, просіть про допомогу.

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