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


85

Назовите соображения дизайна при выборе между использованием одноэлементного или статического класса. При этом вы как бы вынуждены противопоставлять их, поэтому любые контрасты, которые вы можете придумать, также полезны для демонстрации вашего мыслительного процесса! Также каждый интервьюер любит видеть наглядные примеры. :)


класс java не может быть статическим, если это не внутренний класс
Харшана

1
Если состояние объекта имеет значение, используйте синглтон, в противном случае используйте статический класс.
Левент Дивилиоглу 07

Ответы:


80
  • Синглтоны могут реализовывать интерфейсы и наследовать от других классов.
  • Синглтоны могут загружаться лениво. Только тогда, когда это действительно необходимо. Это очень удобно, если инициализация включает дорогостоящую загрузку ресурсов или подключения к базе данных.
  • Синглтоны предлагают реальный объект.
  • Синглтоны могут быть расширены до фабрики. Скрытое управление объектами является абстрактным, поэтому его легче обслуживать и улучшать код.

2
Взгляните также на эту ссылку: codeofdoom.com/wordpress/2008/04/20/…
Амит,

4
«Синглтоны могут быть загружены лениво» - в C # статический конструктор вызывается только при первой ссылке на статический член. В PHP вы можете использовать автозагрузку, чтобы она запускалась только при первом использовании класса.
mpen

Статические инициализации Java тоже ленивы. Никаких очков за синглтоны нет.
MikeFHay

13

Как насчет «избегать обоих»? Синглтоны и статические классы:

  • Может ввести глобальное состояние
  • Будьте тесно связаны с несколькими другими классами
  • Скрыть зависимости
  • Может затруднить изолирование классов модульного тестирования

Вместо этого изучите библиотеки Injection Injection и Inversion of Control Container . Несколько библиотек IoC будут управлять жизненным циклом за вас.

(Как всегда, есть исключения, такие как статические математические классы и методы расширения C #.)


1
Маленькие гниды: одноэлементные классы совместимы с методами внедрения зависимостей (просто вставьте их в их зависимые классы, и им не нужно ничего жестко кодировать) - это позволяет изолированно издеваться и тестировать. Они также являются допустимым шаблоном проектирования для управления ресурсами, которые, как известно, ограничены для всей платформы. (Классический пример - принтеры, у которых нет управления конфликтами.)
cdleary

@cdleary - согласился; однако классическая реализация часто оставляет Singleton.getInstance () по всей кодовой базе. «Синглтоны» для управления ресурсами, кешами и т. Д. Могут быть реализованы с помощью фреймворков POCO / POJO и контейнеров IoC, которые поддерживают управление временем жизни (зарегистрируйте тип как время жизни контейнера, и каждое разрешение получит один и тот же экземпляр).
TrueWill

9
Если вы хотите надлежащим образом похоронить своих синглтонов, перейдите на singletonfuneralservice.com
TrueWill

1
Hide Dependenciesнебольшая дополнительная информация по этому поводу: вы можете реализовать шаблон инверсии управления, например, с помощью Ninject . Это позволяет вам использовать только один экземпляр типа, SingletonScopeпривязав его к a , что означает, что он всегда будет вводить один и тот же экземпляр, но класс не будет одноэлементным.
LuckyLikey

8

Я бы сказал, что единственная разница заключается в синтаксисе: MySingleton.Current.Whatever () vs MySingleton.Whatever (). Состояние, как упомянул Дэвид, в конечном счете «статично» в любом случае.


РЕДАКТИРОВАТЬ: бригада захоронения пришла с digg ... во всяком случае, я подумал о случае, который потребует сингла. Статические классы не могут наследовать от базового класса или реализовывать интерфейс (по крайней мере, в .Net они не могут). Поэтому, если вам нужна эта функция, вы должны использовать синглтон.


7

Одно из моих любимых обсуждений этой проблемы здесь (исходный сайт отключен, теперь связан с Internet Archive Wayback Machine ).

Подводя итог преимуществ гибкости синглтона:

  • Синглтон можно легко превратить в фабрику
  • Синглтон можно легко изменить, чтобы он возвращал разные подклассы
  • это может привести к более удобному обслуживанию приложения

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: создание экземпляра синглтона можно контролировать.
Никит Батале

3
возможно, лучшим словом было бы инициализация (а не создание экземпляра)
Марлон

3

Синглтоны не следует использовать так же, как статические классы. По сути,

MyStaticClass.GetInstance().DoSomething();

по сути то же самое, что

MyStaticClass.DoSomething();

На самом деле вам следует рассматривать синглтон как просто другой объект . Если службе требуется экземпляр одноэлементного типа, передайте этот экземпляр в конструктор:

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

Служба не должна знать, что объект является одноэлементным, и должна рассматривать объект как просто объект.

Объект, безусловно, может быть реализован как деталь реализации и как аспект общей конфигурации, как синглтон, если это упростит задачу. Но вещи, которые используют объект, не должны знать, является ли объект синглтоном или нет.


2

Если под «статическим классом» вы подразумеваете класс, который имеет только статические переменные, то они действительно могут поддерживать состояние. Насколько я понимаю, единственная разница будет заключаться в способе доступа к этой вещи. Например:

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

против

MySingleton.doSomething();

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


2
Синглтоны сами по себе являются объектами -> их можно передавать как аргументы.
Кристиан Клаузер,

2

Шаблон Singleton обычно используется для обслуживания независимых от экземпляра или статических данных, когда несколько потоков могут обращаться к данным одновременно. Одним из примеров могут быть коды штатов.


1

Синглтоны никогда не должны использоваться (если вы не считаете класс без изменяемого состояния одиночным). "статические классы" не должны иметь изменяемого состояния, кроме, возможно, поточно-безопасных кешей и т.п.

Практически любой пример синглтона показывает, как этого не делать.


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

0

Если синглтон - это то, от чего вы можете избавиться, чтобы очистить его после этого, вы можете рассматривать его, когда это ограниченный ресурс (т.е. только один из них), который вам не нужен все время, и у вас есть какая-то память или стоимость ресурса при его распределении.

Код очистки выглядит более естественно, когда у вас есть синглтон, в отличие от статического класса, содержащего поля статического состояния.

Однако код в любом случае будет выглядеть одинаково, поэтому, если у вас есть более конкретные причины для запроса, возможно, вам следует уточнить.


0

Эти два могут быть очень похожими, но помните, что настоящий синглтон должен сам быть создан (предоставлен один раз), а затем обслуживаться. Класс базы данных PHP, который возвращает экземпляр, на mysqliсамом деле не является синглтоном (как его называют некоторые люди), потому что он возвращает экземпляр другого класса, а не экземпляр класса, который имеет экземпляр как статический член.

Итак, если вы пишете новый класс, который планируете разрешить только один экземпляр в своем коде, вы можете также написать его как синглтон. Подумайте об этом как о написании обычного класса и добавлении к нему, чтобы облегчить требование создания одного экземпляра. Если вы используете чужой класс, который не можете изменить (например, mysqli), вы должны использовать статический класс (даже если вы не можете указать префикс его определения с помощью ключевого слова).


0

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


0

Статические классы нельзя передавать как аргументы; экземпляры синглтона могут быть. Как упоминалось в других ответах, следите за проблемами потоковой передачи со статическими классами.

rp


0

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

После получения ссылки на одноэлементный объект его можно использовать так же, как и любой другой объект. Клиентскому коду может даже не потребоваться знать, что он использует синглтон, если ссылка на синглтон хранится ранее:

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

Это, очевидно, упрощает задачу, когда вы решаете отказаться от шаблона Singleton в пользу реального шаблона, такого как IoC.


0

Используйте шаблон singleton, когда вам нужно вычислить что-то во время выполнения, что вы бы вычислили во время компиляции, если бы вы могли, например таблицы поиска.


0

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


0

Синглтон также является хорошей идеей, если вы хотите принудительно кэшировать данные. например, у меня есть класс, который ищет определения в XML-документе. Поскольку синтаксический анализ документа может занять некоторое время, я установил кеш определений (я использую SoftReferences, чтобы избежать ошибок outOfmemeoryErrors). Если желаемого определения нет в кеше, я выполняю дорогостоящий синтаксический анализ xml. В противном случае возвращаю копию из кеша. Поскольку наличие нескольких кешей означало бы, что мне все равно придется загружать одно и то же определение несколько раз, мне нужен статический кеш. Я решил реализовать этот класс как одноэлемент, чтобы я мог писать класс, используя только обычные (нестатические) члены данных. Это позволяет мне по-прежнему создавать определение класса, если он мне понадобится по какой-либо причине (сериализация, модульное тестирование и т. Д.)


0

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

В синглтоне есть код для создания экземпляра фактического объекта, который может оказаться большим подспорьем, если вы столкнетесь с проблемами гонок. В статическом решении вам может потребоваться решение гоночных задач в нескольких местах кода.

Однако так же, как синглтон может быть построен с некоторыми статическими переменными, вы можете сравнить его с goto. Это может быть очень полезно для создания других структур, но вам действительно нужно знать, как его использовать, и не следует «злоупотреблять» им. Поэтому общая рекомендация - придерживаться синглтона и при необходимости использовать static.

также проверьте другой пост: Почему стоит выбирать статический класс вместо одноэлементной реализации?


0

сослаться на это

резюме:

а. Одно простое практическое правило, которому вы можете следовать, - если ему не нужно поддерживать состояние, вы можете использовать статический класс, в противном случае вы должны использовать синглтон.

б. используйте Singleton, если это особенно «тяжелый» объект. Если ваш объект большой и занимает разумный объем памяти, много n / w вызовов (пул соединений) и т. Д. Чтобы убедиться, что его экземпляр не будет создаваться несколько раз. Класс Singleton поможет предотвратить подобные ситуации.


-1

Когда одному классу нужно состояние. Синглтоны поддерживают глобальное состояние, а статические классы - нет.

Например, создание помощника для класса реестра: если у вас есть изменяемый куст (текущий пользователь HKey против локального компьютера HKEY), вы можете:

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

Теперь любые дальнейшие вызовы этого синглтона будут работать в кусте локальной машины. В противном случае, используя статический класс, вам нужно будет указать каждый куст локального компьютера или иметь такой метод, как ReadSubkeyFromLocalMachine.


1
Я бы поспорил против использования синглтона таким образом ... Очень опасно сохранять состояния, потому что следующий вызывающий может забыть установить куст и записать в реестр не тот улей ... :-( Это вероятно, пример неправильного использования, из-за которого люди думают, что синглтоны плохие.
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.