Существуют ли жизнеспособные альтернативы синглтон-модели GOF? - PullRequest
87 голосов
/ 02 октября 2008

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

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

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

Какая еще у вас идея?

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

Ответы [ 16 ]

92 голосов
/ 02 октября 2008

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

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

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

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

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

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-инфраструктуру, или вы можете сделать это вручную, но важно избавиться от вашего глобального состояния и сделать явными зависимости и взаимодействия.

72 голосов
/ 02 октября 2008

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

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

  1. Создание интерфейса и реализация по умолчанию вашего синглтона
  2. Создайте один экземпляр вашей реализации по умолчанию на «вершине» вашей системы. Это может быть в конфигурации Spring, или в коде, или определяться различными способами в зависимости от вашей системы.
  3. Передача отдельного экземпляра в каждый компонент, который нуждается в этом (внедрение зависимости)
14 голосов
/ 02 октября 2008

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

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

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

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.

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

8 голосов
/ 02 октября 2008

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

8 голосов
/ 02 октября 2008

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

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

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

4 голосов
/ 17 марта 2010

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 имеет схожее с синглтоном поведение, но делает это так, что программисту необязательно известно о факте использования синглтона.

4 голосов
/ 02 октября 2008

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

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

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

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

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

*pseudocode* singletonType.Instance

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

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

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

2 голосов
/ 02 октября 2008

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

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

1 голос
/ 02 октября 2008

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

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

0 голосов
/ 12 ноября 2008

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...