Коли Сінглтон підходить? [зачинено]


Відповіді:


32

Дві головні закиди Сінглтона потрапляють у два табори з того, що я спостерігав:

  1. Синглтони зловживають та зловживають менш працездатними програмістами, тому все стає синглтоном, і ви бачите код, заповнений посиланнями Class :: get_instanc (). Взагалі кажучи, є лише один або два ресурси (наприклад, підключення до бази даних), які відповідають умовам використання шаблону Singleton.
  2. Однотонні по суті є статичними класами, спираючись на один або кілька статичних методів та властивостей. Усі статичні речі представляють реальні, відчутні проблеми, коли ви намагаєтесь зробити тестування одиниць, тому що вони представляють тупики у вашому коді, які не можна знущатися чи заглушувати. Як результат, під час тестування класу, який спирається на Singleton (або будь-який інший статичний метод чи клас), ви не тільки тестуєте цей клас, але і статичний метод або клас.

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


6
Я просто хочу сказати, що ви можете знущатися над статичними методами на Java за допомогою JMockit ( code.google.com/p/jmockit ). Це дуже зручний інструмент.
Майкл К

3
Деякі думки: контейнер - синглтон, тому ти не позбувся синглтона. Крім того, якщо ви пишете бібліотеку із загальнодоступним API, ви не можете змусити користувача вашого API використовувати ваш контейнер. Якщо вам потрібен глобальний менеджер ресурсів у вашій бібліотеці (наприклад, захист доступу до одного фізичного ресурсу), тоді вам потрібно використовувати Singleton.
Скотт Вітлок

2
@Scott Whitlock Чому це потрібно бути одиноким? Просто тому, що є лише один екземпляр, це не так. Будь-яка бібліотека IoC керуватиме такою залежністю ...
MattDavey

Це рішення пропонується також відомим як Toolbox
cregox

41

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

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


Бажаю, щоб я міг подати заявку не раз!
MattDavey

34

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

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


3
Отже, іншими словами, ви проти будь-якого використання бази даних у програмі.
Kendall Helmstetter Gelner

Існує відоме відео для спілкування з google tech, яке повторює вашу думку youtube.com/watch?v=-FRm3VPhseI
Martin York

2
@KendallHelmstetterGelner: Є різниця між станом та вторинним сховищем. Навряд чи ви зможете зберегти цілу БД в пам'яті.
Мартін Йорк

7

Чому люди його використовують?

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

  1. Один екземпляр .

    "Використовуйте один екземпляр класу C у всій програмі."

    Це розумна вимога, наприклад, для "підключення до бази даних за замовчуванням". Це не означає, що ви ніколи не створите друге db-з'єднання, це просто означає, що ви зазвичай працюєте з типовим.

  2. Єдина інстанція .

    Msgstr "Не дозволяти копіювати клас C більше одного разу (за процес, за запитом тощо)."

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

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

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

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

  3. Глобальний доступ.

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

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

  4. Ледача інстанція.

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

Типова реалізація

Типовою реалізацією є клас із приватним конструктором та статичною змінною екземпляра та методом статичного getInstance () з лінивою інстанцією.

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

Висновок

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

Однак є випадки використання, коли у вас залишаються такі дійсні вимоги:

  • Одиничний екземпляр (але не одна інстанція)
  • Глобальний доступ (тому що ви працюєте з бронзовим віком)
  • Ледачий примірник (адже це просто приємно мати)

Отже, ось що ви можете зробити в цьому випадку:

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

  • Створіть окремий клас S зі статичною змінною екземпляра та статичним методом S :: getInstance () з ледачим інстанцією, який буде використовувати клас C для екземпляра.

  • Виключіть усі побічні ефекти від конструктора C. Замість цього покладіть ці побічні ефекти у метод S :: getInstance ().

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

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

  • За бажанням створіть інтерфейс, який реалізує клас C, і використовуйте це для документування зворотного значення S :: getInstance ().

(Ми все ще називаємо це одинаком? Я залишаю це в розділі коментарів.)

Переваги:

  • Ви можете створити окремий екземпляр C для тестування одиниць, не торкаючись жодного глобального стану.

  • Управління екземплярами відокремлено від самого класу -> розділення проблем, принцип єдиної відповідальності.

  • Було б досить просто дозволити S :: getInstance () використовувати інший клас для екземпляра або навіть динамічно визначати, який клас використовувати.


1

Особисто я буду використовувати одиночні кнопки, коли мені потрібно 1, 2 або 3, або деяка обмежена кількість об'єктів для конкретного класу, про який йдеться. Або я хочу донести користувачеві мого класу, що я не хочу створити кілька примірників мого класу, щоб він нормально функціонував.

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

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

Що стосується того, коли не використовувати його, дивіться вище 3 та змініть їх на протилежне.


3
-0, але я майже -1. Я вважаю за краще передавати його скрізь у своєму коді, а не використовувати сингл, АЛЕ якщо я дуже ледачий, я можу це зробити. Я б замість цього застосував глобальні параметри, якщо я в змозі статичних членів. Глобальна версія дійсно набагато краща (для мене), ніж одиночні. Глобали не брешуть
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.