Существуют ли жизнеспособные альтернативы синглтон-модели GOF?

StackOverflow https://stackoverflow.com/questions/162042

  •  03-07-2019
  •  | 
  •  

Вопрос

Посмотрим правде в глаза. Шаблон Singleton - это весьма спорная тема с программистами полчищ на обе стороны забора. Есть те, кто чувствует, что Синглтон - не что иное, как прославленная глобальная переменная, и другие, которые клянутся по шаблону и непрерывно используют его. Я не хочу, чтобы Одиночная борьба лежал в основе моего вопроса, однако. Каждый может вступить в перетягивание каната и сразиться с ним, чтобы узнать, кто победит, несмотря на все мои заботы . То, что я пытаюсь сказать, - я не верю, что есть единственный правильный ответ, и я не намеренно пытаюсь разжечь партизанские разногласия. Меня просто интересуют синглтон-альтернативы , когда я задаю вопрос:

Есть ли у них какие-либо конкретные альтернативы синглтонскому шаблону GOF?

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

Какие у вас есть другие идеи?

РЕДАКТИРОВАТЬ . Я не хочу, чтобы это был еще один пост о том, как правильно использовать синглтон. " Опять же, я ищу способы избежать этого. Ради прикола, хорошо? Я полагаю, что задаю чисто академический вопрос вашим лучшим трейлером фильма "В параллельной вселенной, где нет синглтона, что мы могли бы сделать?"

Это было полезно?

Решение

Алекс Миллер в шаблонах, которые я ненавижу " ; цитирует следующее:

" Когда синглтон кажется ответом, я нахожу, что часто разумнее:

<Ол>
  • Создайте интерфейс и реализацию вашего синглтона по умолчанию
  • Создайте один экземпляр вашей реализации по умолчанию в & # 8220; top & # 8221; вашей системы. Это может быть в конфигурации Spring, или в коде, или определяться различными способами в зависимости от вашей системы.
  • Передайте отдельный экземпляр в каждый компонент, который нуждается в этом (внедрение зависимости)
  • Другие советы

    Чтобы понять правильный способ обхода синглетонов, вам необходимо понять, что не так с синглетонами (и глобальным состоянием в целом):

    Синглтоны скрывают зависимости.

    Почему это важно?

    Потому что Если вы скрываете зависимости, вы, как правило, теряете количество связей.

    Вы можете утверждать, что

    void purchaseLaptop(String creditCardNumber, int price){
      CreditCardProcessor.getInstance().debit(creditCardNumber, amount);
      Cart.getInstance().addLaptop();
    }
    

    проще чем

    void purchaseLaptop(CreditCardProcessor creditCardProcessor, Cart cart, 
                        String creditCardNumber, int price){
      creditCardProcessor.debit(creditCardNumber, amount);
      cart.addLaptop();
    }
    

    но, по крайней мере, второй API проясняет, кто именно является соавторами метода.

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

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

    Я думаю, это было бы сложно, но после прочтения этого поста в блоге " Куда ушли все синглтоны?" , это кажется естественным. Кроме того, это очень помогает изолировать ваши юнит-тесты.

    Итак, что вам нужно сделать? Всякий раз, когда объект зависит от другого, он будет получать его экземпляр только через конструктор (без нового ключевого слова в вашем классе).

    class NeedyClass {
    
        private ExSingletonClass exSingleton;
    
        public NeedyClass(ExSingletonClass exSingleton){
            this.exSingleton = exSingleton;
        }
    
        // Here goes some code that uses the exSingleton object
    }
    

    А потом, фабрика.

    class FactoryOfNeedy {
    
        private ExSingletonClass exSingleton;
    
        public FactoryOfNeedy() {
            this.exSingleton = new ExSingletonClass();
        }
    
        public NeedyClass buildNeedy() {
            return new NeedyClass(this.exSingleton);
        }
    }
    

    Поскольку вы создадите экземпляр своей фабрики только один раз, будет один экземпляр exSingleton. Каждый раз, когда вы вызываете buildNeedy, новый экземпляр NeedyClass будет связан с exSingleton.

    Надеюсь, это поможет. Пожалуйста, укажите на любые ошибки.

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

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

    Я не избегаю Синглтон Паттерна Или это уместно, и я использую это, или это не уместно, и я не использую это. Я считаю, что это так просто.

    Уместность (или ее отсутствие) синглтона зависит от ситуации. Это проектное решение, которое должно быть принято, и последствия этого решения должны быть поняты (и задокументированы).

    Шаблон singleton существует, поскольку существуют ситуации, когда один объект необходим для предоставления набора услуг .

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

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

    *pseudocode* currentContainer.GetServiceByObjectType(singletonType)
    //Under the covers the object might be a singleton, but this is hidden to the consumer.
    

    вместо одного глобального

    *pseudocode* singletonType.Instance
    

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

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

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

    Monostate (описанный в Agile Software Development Роберта Мартина) является альтернативой синглтону. В этом шаблоне все данные класса являются статическими, но методы получения / установки не являются статичными.

    Например:

    public class MonoStateExample
    {
        private static int x;
    
        public int getX()
        {
            return x;
        }
    
        public void setX(int xVal)
        {
            x = xVal;
        }
    }
    
    public class MonoDriver
    {
        public static void main(String args[])
        {
            MonoStateExample m1 = new MonoStateExample();
            m1.setX(10);
    
            MonoStateExample m2 = new MonoStateExample();
            if(m1.getX() == m2.getX())
            {
                //singleton behavior
            }
        }
    }
    

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

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

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

    Если ваша проблема заключается в том, что вы хотите сохранить состояние, вам нужен класс MumbleManager. Перед началом работы с системой ваш клиент создает MumbleManager, где Mumble - это имя системы. Государство сохраняется через это. Скорее всего, ваш MumbleManager будет содержать набор свойств, в котором хранится ваше состояние.

    Этот тип стиля очень похож на C и не очень похож на объект - вы обнаружите, что все объекты, которые определяют вашу систему, будут иметь ссылку на один и тот же MumbleManager.

    Используйте простой объект и фабричный объект. Фабрика отвечает за применение политик к экземпляру и простым объектам только с информацией о конфигурации (например, в ней) и поведением.

    На самом деле, если вы разрабатываете с нуля, избегая Singelton, вам, возможно, не придется обходиться без использования Singletons с помощью статических переменных. При использовании статических переменных вы также более или менее создаете Singleton, единственное отличие состоит в том, что вы создаете разные экземпляры объектов, однако внутренне все они ведут себя так, как если бы они использовали Singleton.

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

    Кстати, лично у меня нет проблем с синглетонами, и я не могу понять проблемы, с которыми сталкиваются другие люди в отношении синглетонов. Я не вижу в них ничего плохого. То есть если вы ими не злоупотребляете. Любой полезный метод может быть нарушен, и в случае злоупотребления это приведет к отрицательным результатам. Другой метод, который обычно используется неправильно - наследование. Тем не менее никто не сказал бы, что наследование является чем-то плохим только потому, что некоторые люди ужасно злоупотребляют им.

    Лично для меня & # 1072; Гораздо более разумным способом реализации чего-то, что ведет себя как синглтон, является использование полностью статического класса (статические члены, статические методы, статические свойства). Большую часть времени я реализую это таким образом (я не могу думать о каких-либо различиях в поведении с точки зрения пользователя)

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

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

    И да, «полиция» - это слово, которое я здесь имел в виду, а не «избегать». Синглтон - это не то, чего следует избегать (так же, как и goto и глобальные переменные - это не то, чего следует избегать). Вместо этого вы должны следить за его использованием и убедиться, что это лучший способ добиться того, что вы хотите сделать эффективно.

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

    Не программируя в сильно объектно-ориентированной среде (например, на Java), я не совсем разбираюсь в тонкостях обсуждения. Но я реализовал синглтон в PHP 4. Я сделал это как способ создания обработчика базы данных «черного ящика», который автоматически инициализировался и не должен был пропускать вызовы функций вверх и вниз в неполной и несколько сломанной структуре.

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

    Как и большинство шаблонов и алгоритмов, использование синглтона «просто потому, что это круто» - это неправильное решение. Мне нужен был действительно «черный ящик», который очень похож на синглтон. И IMO - это способ решить вопрос: знать о шаблоне, но также смотреть на его более широкий охват и на каком уровне его экземпляр должен быть уникальным.

    Что вы имеете в виду, каковы мои методы, чтобы избежать этого?

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

    Но нет. Мне не нужно избегать синглтон-паттернов. Это просто не возникает.

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top