Синглтон: как его использовать - PullRequest
288 голосов
/ 17 сентября 2008

Edit: От другого вопроса я предоставил ответ, который содержит ссылки на множество вопросов / ответов о синглетонах: Подробнее о синглетонах здесь:

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

Я вижу синглтоны как шаблон дизайна (хороший и плохой).

Проблема синглтона не в шаблоне, а в пользователях (извините всех). Все и их отец думают, что могут правильно их реализовать (а из многих интервью, которые я провел, большинство людей не могут). Кроме того, поскольку все думают, что они могут реализовать правильный Singleton, они используют шаблон и используют его в ситуациях, которые не подходят (замена глобальных переменных на Singletons!).

Итак, основные вопросы, на которые необходимо ответить:

  • Когда следует использовать синглтон
  • Как правильно реализовать Singleton

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


Так что качайте шарик:
Я подниму руку и скажу, что это то, что я использую, но, вероятно, у меня проблемы.
Мне нравится, как Скотт Майерс рассматривает эту тему в своих книгах "Эффективный C ++"

Хорошие ситуации для использования синглетонов (не много):

  • Каркасы логирования
  • Нитки для утилизации бассейнов
/*
 * C++ Singleton
 * Limitation: Single Threaded Design
 * See: http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
 *      For problems associated with locking in multi threaded applications
 *
 * Limitation:
 * If you use this Singleton (A) within a destructor of another Singleton (B)
 * This Singleton (A) must be fully constructed before the constructor of (B)
 * is called.
 */
class MySingleton
{
    private:
        // Private Constructor
        MySingleton();
        // Stop the compiler generating methods of copy the object
        MySingleton(MySingleton const& copy);            // Not Implemented
        MySingleton& operator=(MySingleton const& copy); // Not Implemented

    public:
        static MySingleton& getInstance()
        {
            // The only instance
            // Guaranteed to be lazy initialized
            // Guaranteed that it will be destroyed correctly
            static MySingleton instance;
            return instance;
        }
};

OK. Давайте вместе возьмем критику и другие реализации.
: -)

Ответы [ 24 ]

0 голосов
/ 02 марта 2011

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

Синглтон - это реализация для глобально доступного объекта (отныне GAO), хотя не все GAO являются синглетонами.

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

Ленивая загрузка / отложенная оценка - это другое понятие, и синглтон, как правило, также реализует это. У него много собственных проблем, в частности, безопасности потоков и проблем, если он терпит неудачу с исключениями, так что то, что казалось хорошей идеей в то время, оказывается не таким уж большим. (Немного похоже на реализацию COW в строках).

Имея это в виду, GOA можно инициализировать так:

namespace {

T1 * pt1 = NULL;
T2 * pt2 = NULL;
T3 * pt3 = NULL;
T4 * pt4 = NULL;

}

int main( int argc, char* argv[])
{
   T1 t1(args1);
   T2 t2(args2);
   T3 t3(args3);
   T4 t4(args4);

   pt1 = &t1;
   pt2 = &t2;
   pt3 = &t3;
   pt4 = &t4;

   dostuff();

}

T1& getT1()
{
   return *pt1;
}

T2& getT2()
{
   return *pt2;
}

T3& getT3()
{
  return *pt3;
}

T4& getT4()
{
  return *pt4;
}

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

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

0 голосов
/ 17 сентября 2008

Анти-использование:

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

0 голосов
/ 17 сентября 2008

Я думаю, что это самая надежная версия для C #:

using System;
using System.Collections;
using System.Threading;

namespace DoFactory.GangOfFour.Singleton.RealWorld
{

  // MainApp test application

  class MainApp
  {
    static void Main()
    {
      LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b4 = LoadBalancer.GetLoadBalancer();

      // Same instance?
      if (b1 == b2 && b2 == b3 && b3 == b4)
      {
        Console.WriteLine("Same instance\n");
      }

      // All are the same instance -- use b1 arbitrarily
      // Load balance 15 server requests
      for (int i = 0; i < 15; i++)
      {
        Console.WriteLine(b1.Server);
      }

      // Wait for user
      Console.Read();    
    }
  }

  // "Singleton"

  class LoadBalancer
  {
    private static LoadBalancer instance;
    private ArrayList servers = new ArrayList();

    private Random random = new Random();

    // Lock synchronization object
    private static object syncLock = new object();

    // Constructor (protected)
    protected LoadBalancer()
    {
      // List of available servers
      servers.Add("ServerI");
      servers.Add("ServerII");
      servers.Add("ServerIII");
      servers.Add("ServerIV");
      servers.Add("ServerV");
    }

    public static LoadBalancer GetLoadBalancer()
    {
      // Support multithreaded applications through
      // 'Double checked locking' pattern which (once
      // the instance exists) avoids locking each
      // time the method is invoked
      if (instance == null)
      {
        lock (syncLock)
        {
          if (instance == null)
          {
            instance = new LoadBalancer();
          }
        }
      }

      return instance;
    }

    // Simple, but effective random load balancer

    public string Server
    {
      get
      {
        int r = random.Next(servers.Count);
        return servers[r].ToString();
      }
    }
  }
}

Вот .NET-оптимизированная версия :

using System;
using System.Collections;

namespace DoFactory.GangOfFour.Singleton.NETOptimized
{

  // MainApp test application

  class MainApp
  {

    static void Main()
    {
      LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b4 = LoadBalancer.GetLoadBalancer();

      // Confirm these are the same instance
      if (b1 == b2 && b2 == b3 && b3 == b4)
      {
        Console.WriteLine("Same instance\n");
      }

      // All are the same instance -- use b1 arbitrarily
      // Load balance 15 requests for a server
      for (int i = 0; i < 15; i++)
      {
        Console.WriteLine(b1.Server);
      }

      // Wait for user
      Console.Read();    
    }
  }

  // Singleton

  sealed class LoadBalancer
  {
    // Static members are lazily initialized.
    // .NET guarantees thread safety for static initialization
    private static readonly LoadBalancer instance =
      new LoadBalancer();

    private ArrayList servers = new ArrayList();
    private Random random = new Random();

    // Note: constructor is private.
    private LoadBalancer()
    {
      // List of available servers
      servers.Add("ServerI");
      servers.Add("ServerII");
      servers.Add("ServerIII");
      servers.Add("ServerIV");
      servers.Add("ServerV");
    }

    public static LoadBalancer GetLoadBalancer()
    {
      return instance;
    }

    // Simple, but effective load balancer
    public string Server
    {
      get
      {
        int r = random.Next(servers.Count);
        return servers[r].ToString();
      }
    }
  }
}

Вы можете найти этот паттерн на dotfactory.com .

0 голосов
/ 17 сентября 2008

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

Редактировать - конечно, они должны быть доступны только для чтения!

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