Коли слід використовувати шаблон singleton замість статичного класу? [зачинено]


85

Назвіть міркування щодо проектування при виборі між використанням одиночного та статичного класу. Роблячи це, ви начебто змушені протиставляти обидва, тому будь-які контрасти, які ви можете придумати, також корисні для показу вашого процесу мислення! Також кожен інтерв’юер любить бачити наочні приклади. :)


клас Java не може стати статичним, якщо це не внутрішній клас
Харшана,

1
Якщо стан об'єкта має значення, використовуйте синглтон, інакше використовуйте статичний клас.
Левент Дівіліоглу

Відповіді:


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

2
Погляньте також на це посилання: codeofdoom.com/wordpress/2008/04/20/…
Amit

4
"Одиночні завантаження можуть бути лінивими" - у C # статичний конструктор викликається лише при першому посиланні на статичний член. У PHP ви можете використовувати автоматичне завантаження, щоб воно запускалося лише при першому використанні класу.
mpen

Статична ініціалізація Java також лінива. Там немає балів для одиноких.
MikeFHay

13

Як щодо "уникати обох"? Одиночні та статичні класи:

  • Може запровадити глобальну державу
  • Будьте тісно пов’язані з кількома іншими класами
  • Приховати залежності
  • Може ускладнити одиничні класи тестування окремо

Натомість перегляньте бібліотеки інжекції залежностей та інверсії контрольних контейнерів . Декілька бібліотек IoC допоможуть вам керувати життям.

(Як завжди, є винятки, такі як статичні математичні класи та методи розширення C #.)


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

@cdleary - погоджено; однак класична реалізація часто залишає Singleton.getInstance () у всій кодовій базі. "Синглтони" для управління ресурсами, кешами тощо можуть бути реалізовані за допомогою POCO / POJOs та IoC Container framework, які підтримують управління тривалістю життя (зареєструйте тип як час життя контейнера, і кожна роздільна здатність отримає однаковий екземпляр).
TrueWill

9
Якщо ви хочете належним чином поховати своїх
синглтонів

1
Hide Dependenciesневелика допоміжна інформація щодо цього: Ви можете реалізувати шаблон інверсії контролю, наприклад, використовуючи Ninject . Це дозволяє використовувати тільки один екземпляр типу шляхом зв'язування його до SingletonScope, що означає , що він alaways INJECT той же примірник, але класи не буде Сінглтон.
LuckyLikey

8

Я б стверджував, що єдина різниця - це синтаксис: MySingleton.Current.Whatever () проти MySingleton.Whatever (). Держава, як згадував Девід, в кінцевому рахунку є "статичною" в будь-якому випадку.


РЕДАКТУВАТИ: Бригада поховань прийшла з Digg ... у будь-якому випадку, я думав про справу, яка вимагала б синглтона. Статичні класи не можуть успадковувати базовий клас і не реалізовувати інтерфейс (принаймні в .Net вони не можуть). Отже, якщо вам потрібна ця функціональність, тоді ви повинні використовувати синглтон.


7

Одне з моїх улюблених обговорень з цього питання - тут (оригінальний сайт не працює, тепер він пов’язаний з Інтернет-архівом Wayback Machine .)

Підсумовуючи переваги гнучкості Singleton:

  • синглтона можна легко перетворити на завод
  • Singleton можна легко змінити, щоб повернути різні підкласи
  • це може призвести до більш ремонтопридатного застосування

5

Статичний клас із навантаженням статичних змінних - трохи хак.

/**
 * Grotty static semaphore
 **/
 public static class Ugly {

   private static int count;

   public synchronized static void increment(){
        count++;
   }

   public synchronized static void decrement(){
        count--;
        if( count<0 ) {
            count=0;
        }
   }

   public synchronized static boolean isClear(){
         return count==0;    

    }
   }

Краще одиничне з фактичним екземпляром.

/**
 * Grotty static semaphore
 **/
 public static class LessUgly {
   private static LessUgly instance;

   private int count;

   private LessUgly(){
   }

   public static synchronized getInstance(){
     if( instance==null){
        instance = new LessUgly();
     }
     return instance;
   }
   public synchronized void increment(){
        count++;
   }

   public synchronized void decrement(){
        count--;
        if( count<0 ) {
            count=0;
        }
   }

   public synchronized boolean isClear(){
         return count==0;    

    }
   }

Штат знаходиться ТІЛЬКИ в інстанції.

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

public static class LessUgly {
       private static Hashtable<String,LessUgly> session;
       private static FIFO<LessUgly> freePool = new FIFO<LessUgly>();
       private static final POOL_SIZE=5;
       private int count;

       private LessUgly(){
       }

       public static synchronized getInstance(){
         if( session==null){
            session = new Hashtable<String,LessUgly>(POOL_SIZE);
            for( int i=0; i < POOL_SIZE; i++){
               LessUgly instance = new LessUgly();  
               freePool.add( instance)
            }
         }
         LessUgly instance = session.get( Session.getSessionID());
         if( instance == null){
            instance = freePool.read();
         }
         if( instance==null){
             // TODO search sessions for expired ones. Return spares to the freePool. 
             //FIXME took too long to write example in blog editor.
         }
         return instance;
       }     

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

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

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


4

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

ObjectFactory.getInstance().makeObject();

Фабрика об'єктів - це об'єкт, який виконує певну послугу.

На відміну від цього, клас, повний статичних методів, - це сукупність дій, які ви, можливо, захочете виконати, організовані у відповідну групу (Клас). Напр

StringUtils.reverseString("Hello");
StringUtils.concat("Hello", "World");

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


4

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


13
Синглтони теж створюються під час виконання ...
рекурсивно

1
@recursive: Можливим є керування екземпляром Singleton.
Nikit Batale

3
можливо, кращим словом буде ініціалізація (а не інстанціювання)
Марлон,

3

Синглтони не слід використовувати так само, як статичні класи. По суті,

MyStaticClass.GetInstance().DoSomething();

по суті те саме, що

MyStaticClass.DoSomething();

Те, що ви насправді повинні робити, - це поводитися з синглтоном як просто з іншим об’єктом . Якщо послуга вимагає екземпляра одиночного типу, передайте цей екземпляр у конструктор:

var svc = new MyComplexServce(MyStaticClass.GetInstance());

Служба не повинна усвідомлювати, що об’єкт є синглтоном, і повинна розглядати об’єкт як просто об’єкт.

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


2

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

MySingleton().getInstance().doSomething();

проти

MySingleton.doSomething();

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


2
Сінглтони самі по собі є об'єктами -> їх можна передавати як аргументи.
Крістіан Клаузер

2

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


1

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

Практично будь-який приклад синглтона показує, як цього не робити.


4
А як щодо менеджерських класів, що контролюють апаратні ресурси? А як щодо класів файлів журналів?
RJFalconer,

0

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

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

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


0

Ці два можуть бути цілком схожими, але пам’ятайте, що справжній Синглтон сам повинен бути створений (наданий, один раз), а потім поданий. Клас бази даних PHP, який повертає екземплярmysqli , насправді не є Singleton (як його називають деякі люди), оскільки він повертає екземпляр іншого класу, а не екземпляр класу, який має екземпляр як статичний член.

Отже, якщо ви пишете новий клас, для якого плануєте дозволити лише один екземпляр у своєму коді, ви можете написати його як синглтон. Подумайте про це як про написання простого класу jane та додавання до нього для полегшення вимоги до єдиного екземпляру. Якщо ви використовуєте чужий клас, який ви не можете змінити (наприклад mysqli), ви повинні використовувати статичний клас (навіть якщо вам не вдається додати до його визначення ключове слово).


0

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


0

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

rp


0

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

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

Foo foo = Foo.getInstance();
doSomeWork(foo); // doSomeWork wont even know Foo is a singleton

Це, очевидно, полегшує ситуацію, коли ви вирішили відмовитись від шаблону Singleton на користь реального шаблону, такого як IoC.


0

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


0

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


0

Синглтон - також хороша ідея, якщо ви хочете примусити ефективно кешувати дані. наприклад, у мене є клас, який шукає визначення в документі xml. Оскільки розбір документа може зайняти деякий час, я створив кеш визначень (я використовую SoftReferences, щоб уникнути outOfmemeoryErrors). Якщо потрібного визначення немає в кеші, я роблю дорогий синтаксичний розбір xml. В іншому випадку я повертаю копію з кешу. Оскільки наявність декількох кеш-пам’яті означало б, що мені все одно доведеться завантажувати одне і те ж визначення кілька разів, мені потрібно мати статичний кеш. Я вирішив реалізувати цей клас як синглтон, щоб я міг писати клас, використовуючи лише звичайні (нестатичні) члени даних. Це дозволяє мені все-таки створити істанціацію класу, якщо мені це буде потрібно з якихось причин (серіалізація, модульне тестування тощо)


0

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

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

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

також перевірте інший пост: Навіщо обирати статичний клас замість односторонньої реалізації?


0

зверніться до цього

резюме:

a. Одним простим емпіричним правилом, якого ви можете дотримуватися, є те, що якщо йому не потрібно підтримувати стан, ви можете використовувати клас Static, інакше ви повинні використовувати Singleton.

b. використовуйте Singleton, якщо це особливо "важкий" об'єкт. Якщо ваш об’єкт великий і займає розумний обсяг пам’яті, безліч дзвінків із використанням пулу (пул з'єднань) .. тощо. Щоб переконатись, що його не збирається створювати кілька разів. Клас Сінглтон допоможе запобігти такому випадку


-1

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

Наприклад, створення помічника навколо класу реєстру: якщо у вас є вулик, що змінюється (HKey Current User vs HKEY Local Machine), ви можете перейти:

RegistryEditor editor = RegistryEditor.GetInstance();
editor.Hive = LocalMachine

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


1
Я б сперечався проти використання Singleton таким чином ... Дуже небезпечно тримати стан затяжним, тому що наступний абонент може забути встановити вулик і записати в реєстр не в тому вулику ... :-( Це ймовірно, приклад зловживання, що змушує людей думати, що Сінглтонс поганий.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.