Як я можу уникнути виникнення багатьох одиночних клавіш у своїй ігровій архітектурі?


55

Я використовую ігровий движок cocos2d-x для створення ігор. Двигун вже використовує багато однотонних. Якщо хтось ним користувався, то їм слід ознайомитись з деякими з них:

Director
SimpleAudioEngine
SpriteFrameCache
TextureCache
EventDispatcher (was)
ArmatureDataManager
FileUtils
UserDefault

та багато іншого, загалом близько 16 класів. Ви можете знайти подібний список на цій сторінці: Об'єкти Singleton в Cocos2d-html5 v3.0 Але коли я хочу написати гру, мені потрібно набагато більше синглів:

PlayerData (score, lives, ...)
PlayerProgress (passed levels, stars)
LevelData (parameters per levels and level packs)
SocialConnection (Facebook and Twitter login, share, friend list, ...)
GameData (you may obtain some data from server to configure the game)
IAP (for in purchases)
Ads (for showing ads)
Analytics (for collecting some analytics)
EntityComponentSystemManager (mananges entity creation and manipulation)
Box2dManager (manages the physics world)
.....

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

 - bad testability
 - no inheritance available
 - no lifetime control
 - no explicit dependency chain
 - global access (the same as global variables, actually)
 - ....

Деякі думки ви можете знайти тут: https://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons та https://stackoverflow.com/questions/4074154/when-should-the-singleton -паттерн-не-вживаний-крім того-очевидний

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

Я подумав використовувати Singleton-Facade, який матиме екземпляри всіх цих потрібних мені класів, але кожен з них уже не був би однотонним. Це усуне безліч проблем, і у мене був би один сингтон, який би був самим Фасадом. Але в цьому випадку у мене буде ще одна проблема дизайну. Фасад перетвориться на ОБ'ЄКТ БОГУ. Я думаю, що це теж пахне . Тому я не можу знайти хорошого дизайнерського рішення для цієї ситуації. Будь ласка, порадь.


26
Більшість людей, що сперечаються проти Singleton, схоже, не усвідомлюють, що робота, необхідна для розповсюдження деяких даних / функціональних можливостей через увесь код, може стати більш джерелом помилок, ніж використовувати Singleton. Так що в основному вони не порівнюють, просто відкидаючи ==> я підозрюю, що анти-синглтонізм є поставою :-) Так добре, у Singleton є недоліки, тож якщо ви зможете цього уникнути, зробіть це, щоб уникнути проблем, які виникають з ним , тепер, якщо ви не можете, продовжуйте це, не озираючись. Зрештою, Cocos2d, здається, працює чудово зі своїми 16
синглтонами

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

Я припускаю, що ці одинаки потім доступні в усьому світі? Що було б однією з причин уникати цієї закономірності.
Пітер - Відновіть Моніку

Відповіді:


41

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

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

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

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


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

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

Уорді: Чому їм потрібно бути одинокими? Звичайно, в основному ви можете проходити навколо певного екземпляра, але те, що робить сингл однотонним, це те, що ви НЕ МОЖЕТЕ використовувати інший екземпляр. За допомогою ін'єкції ви можете передавати один екземпляр для одного об’єкта та інший для наступного. Те, що ви з будь-якої причини не закінчите це робити, не означає, що не змогли.
parar

3
Вони не одинокі. Вони є звичайними об'єктами, як і все інше, з чітко визначеним власником, який контролює init / очищення. Якщо ви знаходитесь на 10 рівнях глибоко без доступу, можливо, у вас є проблеми з дизайном. Не все потребує або повинен мати доступ до рендерінгу, аудіо тощо. Цей спосіб змушує задуматися над своїм дизайном. Як додатковий бонус для тих, хто тестує свій код (що ??), тестування одиниць також стає набагато простішим.
megadan

2
Так, я вважаю, що введення залежності (з рамкою Spring або без неї) є ідеальним рішенням для вирішення цього питання. Я вважав це чудовою статтею для тих, хто цікавиться, як краще структурувати код, щоб уникнути передачі речей скрізь: misko.hevery.com/2008/10/21/…
megadan

17

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

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

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

Дзвінки типу:

FlyingToasterManager.GetInstance().Toast();
SmokeAlarmManager.GetInstance().Start();

Виглядає так, використовуючи шаблон пошуку ":

SvcLocator.FlyingToaster.Toast();
SvcLocator.SmokeAlarm.Start();

Як ви бачите, кожен Singleton міститься в локаторі послуг.

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

[ EDIT ]: як зазначено в коментарях, сайт gameprogrammingpatterns.com також містить дуже цікавий розділ про Singleton .

Я сподіваюся, що це допомагає.


4
На веб-сайті, пов’язаному з цим документом, gameprogrammingpatterns.com, також є розділ про Singletons, який здається актуальним.
ajp15243

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

2
Чи можете ви детальніше розглянути питання про "правильну ін'єкцію залежності"?
Blue Wizard

17
Це не дуже допомагає проблемі. По суті, багато одинаків об'єдналися в один гігантський синглтон. Жоден з основних структурних проблем не є виправленим або навіть вирішеним, код все ще смердить.
ClassicThunder

4
@classicthunder Хоча це не стосується кожної окремої проблеми, це велике поліпшення порівняно з Singletons, оскільки він вирішує кілька. Зокрема, код, який ви пишете, більше не пов'язаний з одним класом, а швидше інтерфейсом / категорією класів. Тепер ви можете легко обмінятися різними реалізаціями різних служб.
поштовх

12

Читаючи всі ці відповіді, зауваження та статті, особливо ці дві геніальні статті,

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

  1. Ви не програмуєте на реалізації, а на інтерфейси. Це дуже важливо з точки зору директорів ООП. Під час виконання ви можете змінити або навіть відключити послугу за допомогою шаблону Null Object. Це не тільки важливо з точки зору гнучкості, але безпосередньо допомагає при тестуванні. Ви можете відключити, знущатися, також ви можете використовувати шаблон «Декоратор», щоб також додати деякі журнали та інші функціональні можливості. Це дуже важлива властивість, якої Сінглтон не має.
  2. Ще одна величезна перевага полягає в тому, що ви можете контролювати термін експлуатації об’єкта. У Singleton ви керуєте лише тим часом, коли об’єкт буде породжений шаблоном Lazy Initialization. Але як тільки об’єкт породжується, він буде жити до кінця програми. Але в деяких випадках ви хочете змінити послугу. Або навіть закрити його. Особливо в іграх ця гнучкість дуже важлива.
  3. Він поєднує / обгортає декілька синглтонів в один компактний API. Я думаю, ви також можете використовувати такі фасади, як Локатори обслуговування, які для однієї операції можуть використовувати одразу кілька сервісів.
  4. Ви можете стверджувати, що ми все ще маємо глобальний доступ, який є основною проблемою дизайну в Singleton. Я погоджуюся, але цей шаблон нам знадобиться лише і лише в тому випадку, якщо ми хочемо отримати глобальний доступ. Ми не повинні скаржитися на глобальний доступ, якщо ми вважаємо, що введення прямої залежності не є зручним для нашої ситуації, і нам потрібен глобальний доступ. Але навіть для цієї проблеми можливе вдосконалення (див. Другу статтю вище). Ви можете зробити всі сервіси приватними, і ви можете успадкувати від класу "Локатор послуг" усі класи, які, на вашу думку, повинні використовувати ці послуги. Це може істотно звузити доступ. І врахуйте, що у випадку Singleton ви не можете використовувати спадщину через приватний конструктор.

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

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

EDIT: Після року роботи та використання прямих DI та проблем із величезними конструкторами та безліччю делегацій я провів більше досліджень і знайшов хорошу статтю, яка розповідає про плюси та мінуси щодо методів DI та Locator послуг. Цитуючи цю статтю ( http://www.martinfowler.com/articles/injection.html ) Мартіна Фаулера, яка пояснює, коли насправді локатори сервісу погані:

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

Але в будь-якому випадку, якщо ви хочете, щоб ми вперше пройшли DI, ви повинні прочитати цю http://misko.hevery.com/2008/10/21/dependency-injection-myth-reference-passing/ статтю (вказав @megadan) , приділяючи дуже ретельну увагу Закону Деметера (LoD) або принципу найменших знань .


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

3

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

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

Тож запитайте себе, що простіше в обслуговуванні.

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

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

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


-1: Ця відповідь неправильно описує однотонну схему, а всі її аргументи описують проблеми з поганою реалізацією шаблону. Належний синглтон - це інтерфейс, і він дозволяє уникнути кожної описаної вами проблеми (включаючи перехід на неоднозначність, що є сценарієм, який GoF вирішує явно). Якщо важко переробити використання синглтона, то рефакторинг на використання явного посилання на об'єкт може бути лише складніше, не менше. Якщо сингтон якимось чином викликає БІЛЬШЕ з'єднання коду, це просто було зроблено неправильно.
Сет Беттін

1

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

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

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

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

Ви повинні бачити одиночне слово як службу замість компонента .

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


1

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

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

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

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

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

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

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

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

Давайте розглянемо ваші заняття потроху:


PlayerData (score, lives, ...)
LevelData (parameters per levels and level packs)
GameData (you may obtain some data from server to configure the game)

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


IAP (for in purchases)
Ads (for showing ads)

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


EntityComponentSystemManager (mananges entity creation and manipulation)

Іншими словами, конструктор і сервісний рівень? Чому цей клас без чітких меж і цілей навіть там на першому місці?


PlayerProgress (passed levels, stars)

Чому прогрес гравця відрізняється від класу гравців? Клас гравця повинен знати, як слідкувати за власним прогресом, якщо ви хочете впровадити відстеження прогресу в іншому класі, ніж клас гравця для розділення відповідальності, тоді PlayerProgress повинен стояти за гравцем.


Box2dManager (manages the physics world)

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


Analytics (for collecting some analytics)
SocialConnection (Facebook and Twitter login, share, friend list, ...)

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

Один із способів уникнути передачі екземплярів цих класів - це використання передачі повідомлень. Замість того, щоб робити прямий дзвінок до екземплярів цих класів, натомість ви асинхронно надсилаєте повідомлення, адресоване службі Analytic and SocialConnection, і ці служби підписуються на отримання цих повідомлень і діють на це повідомлення. Оскільки в черзі подій вже є сингл, виправляючи виклик, ви уникаєте необхідності передавати фактичний екземпляр під час спілкування з однотонним.


-1

Для мене одиночки ВЖЕ завжди погані.

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

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

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

Якщо говорити прямо… Єдиний справжній синглтон - це гра, оскільки вона володіє кожною частиною коду.

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

Редагувати: За запитом ...

Гаразд, це з моєї голови, тому що в даний момент у мене немає свого коду, але, по суті, в корені будь-якої гри - ігровий клас, який справді є одинаковим ... це повинно бути!

Тому я додав наступне ...

class Game
{
   IList<GameService> Services;

   public T GetService<T>() where T : GameService
   {
       return Services.FirstOrDefatult(s => s is T);
   }
}

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

public static class Sys
{
    // only the main game assembly can set this (done by the game ctor)
    // anything can get
    public static Game Game { get; internal set; }
}

І використання ...

З будь-якого місця я можу зробити щось подібне

var tService = Sys.Game.getService<T>();
tService.Something();

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

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

Наприклад ...

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


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

2
@ Wardy Я чітко не розумію, що ви зробили, але це звучить як я описав я Singleton-Facade, і він повинен стати об'єктом БОГ, правда?
Нарек

10
Це просто шаблон Singleton, реалізований на рівні гри. Тому в основному його найцікавіший аспект полягає в тому, щоб ви могли зробити вигляд, що не використовуєте Singleton. Корисно, якщо ваш начальник є з церкви, яка не відповідає.
GameAlchemist

2
Я не впевнений, чим це ефективно відрізняється від використання Singletons. Чи не єдина різниця в тому, наскільки конкретно ви отримуєте доступ до своєї єдиної послуги такого типу? Тобто var tService = Sys.Game.getService<T>(); tService.Something();замість того, щоб пропустити getServiceдеталь і отримати доступ до неї безпосередньо?
Крістіан

1
@Christian: Дійсно, саме це і є антипатерн Service Locator. Мабуть, ця громада все ще вважає, що це рекомендована модель дизайну.
Маг

-1

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

Порівняйте, наприклад, Java HashMapз .NET Dictionary. Обидва є досить схожими, але вони мають ключову відмінність: у Java HashMapважко закодовано equalsта hashCode(він ефективно використовує однотонний компаратор), тоді як .NET Dictionaryможе приймати екземпляр IEqualityComparer<T>параметру конструктора. Вартість експлуатації на утримання IEqualityComparerмінімальна, але складність, яку вона впроваджує, не є.

Оскільки кожен Dictionaryекземпляр інкапсулює an IEqualityComparer, його стан - це не просто відображення між ключами, які він насправді містить, та їх асоційованими значеннями, а натомість відображенням між наборами еквівалентності, визначеними ключами та компаратором, та пов'язаними з ними значеннями. Якщо два примірники d1і d2про Dictionaryпосиланнях дотримуються же IEqualityComparer, можна буде виробляти новий екземпляр d3такої , що для будь-якого x, d3.ContainsKey(x)дорівнюватиме d1.ContainsKey(x) || d2.ContainsKey(x). Якщо, однак, d1і d2можуть бути посилання на різні довільні порівняння рівності, взагалі не буде способу їх злиття, щоб забезпечити виконання цієї умови.

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


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

1
@JoshPetrie: У коментарях до оригінального допису (наприклад, від Magus) було висловлено припущення, що вартість виконання посилань, щоб уникнути використання синглів, була не великою. Як такий, я вважав би доречним смислові витрати на те, щоб кожен об'єкт інкапсулював посилання з метою уникнення синглів. Слід уникати одиночних клавіш у випадках, коли їх можна уникнути дешево і легко, але код, який не вимагає відверто потрібного сингтона, але може зламатися, як тільки декілька екземплярів чогось є гіршим, ніж код, який використовує сингтон.
supercat

2
Відповіді не стосуються коментарів, вони - для прямої відповіді на питання.
ClassicThunder

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