Дизайн шаблонів, які слід уникати [закрито]


105

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

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


Мені просто потрібно помітити чудовий список дизайну Antipatterns deviq.com/antipatterns
Розробник

@casperOne Чому ви закрили це питання? Питання було законним.
bleepzter

Відповіді:


149

Візерунки складні

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

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

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

Принципи щодо моделей

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

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

  • Програма на "інтерфейс", а не на "реалізацію". (Банда з чотирьох 1995: 18)
  • Віддати перевагу композиції об'єкта над успадкуванням класу. (Банда з чотирьох 1995: 20)

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

Звідти ви можете ознайомитися з принципами SOLID, про які був відомий Роберт Сесіль Мартін (ака. Дядько Боб) . Скотт Хензельман опитав дядька Боба в подкасті про наступні принципи :

  • S Ingle Відповідальність Принцип
  • O Закритий принцип
  • L Іськів принцип заміщення
  • Я Принцип поділу інтерфейсу
  • D ependency Принцип інверсії

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


7
+1 Дуже хороша відповідь. Схоже, кожен (новачок) програміст сьогодні знає свої дизайнерські зразки або, принаймні, знає, що вони існують. Але дуже багато людей ніколи не чули, не кажучи вже про застосування, таких абсолютно необхідних принципів, як "Єдина відповідальність" для управління складністю свого коду.
eljenso

21

Найбільше хвилювались самі автори дизайнерських шаблонів - модель "Відвідувач".

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

Альтернативна назва шаблону "Відвідувач" - "Мультидиспетчеризація", оскільки шаблон "Відвідувач" - це те, що ви закінчуєте, коли ви бажаєте використовувати однотипну мову відправлення OO для вибору коду, який потрібно використовувати на основі типу двох (або більше) різних об’єктів.

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

У будь-якому випадку, часто ви стикаєтесь із чимось подібним:

interface IShape
{
    double intersectWith(Triangle t);
    double intersectWith(Rectangle r);
    double intersectWith(Circle c);
}

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

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

Часто, вводячи іншу концепцію, ви можете зменшити кількість комбінацій, які вам потрібно буде писати:

interface IShape
{
    Area getArea();
}

class Area
{
    public double intersectWith(Area otherArea);
    ...
}

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


2
Якщо говорити про відвідувачів, то дядько Боб використовує це "весь час" butunclebob.com/ArticleS.UncleBob.IuseVisitor
Spoike

3
@Paul Hollingsworth Чи можете ви надати посилання на те, де йдеться про те, що автори "Шаблонів дизайну" хвилюються (і чому вони турбуються)?
m3th0dman

16

Синглтонтон - клас, що використовує одиночний X, має залежність від нього, яку важко помітити і важко виділити для тестування.

Вони використовуються дуже часто, тому що вони зручні і легкі для розуміння, але вони можуть дуже ускладнити тестування.

Див. Синглтони - патологічні брехуни .


1
Вони також можуть просто змінити тестування, оскільки вони можуть дати вам одну точку введення об'єкта Mock. Все зводиться до правильного балансу.
Мартін Браун

1
@Martin: Якщо звичайно можна змінити сингелтон для тестування (якщо ви не використовуєте стандартну реалізацію singelton), але як це простіше, ніж пройти тестову реалізацію в конструкторі?
Оріп

14

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

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

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

5
@Paul: Метод шаблонів чудовий при правильному використанні, тобто коли різні частини потребують багато відомостей про ті частини, які цього не роблять. Я вважаю, що стратегія повинна використовуватися, коли базовий код викликає лише спеціальний код, а метод шаблона повинен використовуватися, коли користувальницькому коду властиво потрібно знати про базовий код.
dimimcha

так, dimimcha, я згоден ... до тих пір, як класний дизайнер знає про це.
Пол Холлінгсворт

9

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

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

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

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


7

Я думаю, що Active Record - це надмірно використаний зразок, який заохочує змішування ділової логіки з кодом стійкості. Приховувати реалізацію пам’яті від шару моделі не дуже добре, і прив’язує моделі до бази даних. Існує безліч альтернатив (описаних у PoEAA), таких як табличний шлюз даних, рядковий шлюз даних та Data Mapper, які часто дають краще рішення і, безумовно, допомагають забезпечити кращу абстракцію пам’яті. Крім того , ваша модель не повинна потрібно зберігати в базі даних; як щодо зберігання їх як XML або доступу до них за допомогою веб-служб? Наскільки легко було б змінити механізм зберігання ваших моделей?

Однак, Active Record не завжди є поганим і ідеально підходить для більш простих програм, де інші варіанти будуть надмірними.


1
Ніби правда, але певною мірою залежить від реалізації.
Майк Вудхаус

6

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

Щоб назвати деякі ...

є деякі непрактичні схеми , наприклад, наприклад:

  • Interpreter
  • Flyweight

Є також деякі складніші для розуміння , наприклад:

  • Abstract Factory - Повний абстрактний заводський зразок із родинами створених об’єктів - це не такий вітер, як здається
  • Bridge - Може бути занадто абстрактним, якщо абстракція та реалізація розділені на підрядні, але в деяких випадках є дуже зручною схемою
  • Visitor - Розуміння механізму подвійної диспетчеризації дійсно ОБОВ'ЯЗКОВО

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

  • Singleton - не зовсім поганий зразок, просто ТОО перестарається (часто там, де це не підходить)
  • Observer - чудовий зразок ... просто робить код набагато важчим для читання та налагодження
  • Prototype - перевіряє компілятор перевірок на динамічність (що може бути добре чи погано ... залежить)
  • Chain of responsibility - занадто часто просто примусово / штучно підштовхується до дизайну

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

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

Тепер, що далі ...


Легкий малюнок - це обов'язково в будь-який час, коли ви використовуєте ресурс, часто зображення, не раз. Це не зразок, це рішення.
Cengiz Kandemir

5

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


Цікаво читає. Дякуємо за посилання!
Бомбе

3
Дебіли виробляють неправильний код. Чи дерен із візерунками виробляє гірший код, ніж дерен, який ніколи не бачив зразків? Я не думаю, що вони так роблять. Для розумних людей шаблони надають добре відомий словниковий запас, який полегшує обмін ідеями. Рішення: вивчайте шаблони та працюйте лише з розумними програмістами.
Мартін Браун

Я не думаю, що справжній дебіл може створити гірший код - незалежно від того, яким інструментом вони користуються
1800 ІНФОРМАЦІЯ

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

5

Деякі кажуть, що локатор сервісу - це анти-шаблон.


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

2

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


4
Я все зрозумів у вашій відповіді до того моменту, поки слово "А"
1800 р. ІНФОРМАЦІЯ

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

3
@Spoike: делегати / події є реалізацією шаблону спостерігачів
відкрийте

1
Моя особиста кривда щодо "Обозревателя" полягає в тому, що він може створювати витоки пам'яті на зібраних мовою сміттях. Коли ви закінчите з об'єктом, вам потрібно запам'ятати, об'єкт не очиститься.
Мартін Браун

@orip: так, саме тому ви використовуєте делегатів / подій. ;)
Спойк

2

Доповненням до допису Спойке, «Перейменовлення на візерунки» - це добре прочитане.


Я фактично пов’язаний з каталогом книги в Інтернеті. :)
Spoike

Ой! Я не покладав на це нависати. Насправді, одразу після закінчення питання ця книга мені прийшла в голову, і тоді я побачив вашу відповідь. Я не міг зупинити себе, щоб опублікувати це. :)
Адель Ансарі

0

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

Альтернативи:

  1. для кожної петлі. Ця конструкція присутня в більшості основних мов і може використовуватися для уникнення ітераторів у більшості випадків.

  2. селектори à la LINQ або jQuery. Їх слід використовувати, коли для кожного не підходить, оскільки не всі об'єкти з контейнера повинні бути оброблені. На відміну від ітераторів, селектори дозволяють в одному місці виявити, які об’єкти підлягають обробці.


Я згоден із селекторами. Foreach є ітератором, більшість мов OO забезпечують ітерабельний інтерфейс, який ви реалізуєте, щоб дозволити передбачення.
Ніл Ейткен

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

Ітератори - чудова модель. Антидіаграма буде реалізовувати IEnumerable / IEnumerator без ітератора. Я вважаю, що LINQ стало можливим завдяки yieldітератору. Ерік Уайт має велику дискусію з цього приводу на C # 3.0: blogs.msdn.com/b/ericwhite/archive/2006/10/04/… . Також ознайомтеся з дискусією Джеремі Лікнесса про спільні роботи з ітераторами: wintellect.com/CS/blogs/jlikness/archive/2010/03/23/… .

@Ryan Riley, ітератори - це об'єкти низького рівня, тому їх слід уникати у дизайні та коді високого рівня. Деталі реалізації ітераторів та різних видів селекторів тут не мають значення. Селектори, на відміну від Ітераторів, дозволяють програмісту чітко висловити те, що вони хочуть обробити, і таким чином, щоб вони були на високому рівні.
Володимир Фролов

Fwiw, альтернатива LINQ-подібного синтаксису F # - це "List.map (весело x -> x.Value) xs", що приблизно так само, як і розуміння списку.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.