Singleton C# | Паттерн Одиночка C#

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

Идея паттерна проектирования Одиночка

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

Существует три вида паттернов проектирования:

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

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

Архитектура паттерна Singleton

На рисунке представлена схема структуры класса.

Singleton (Одиночка)
Singleton (Одиночка)
  • Singleton — уникальный статический экземпляр класса
  • getInstance() — метод получения экземпляра класса. Если экземпляр еще не создан, то создает новый.

Логика работы паттерна Singleton (Одиночка)

  1. Добавим в класс закрытое статическое поле, в котором будет находиться основной уникальный экземпляр класса
  2. Создадим статичный метод, используемый для получения уникального экземпляра класса
  3. Реализуем создание уникального экземпляра при первом обращении к нему (так называемая «ленивая инициализация»)
  4. Добавим закрытий конструктор класса
  5. Вызовем создание экземпляра класса с помощью статичного метода

Реализация паттерна проектирования Singleton (Одиночка) на языке C#

Singleton.cs
using System;

namespace Patterns.Singleton
{
    /// <summary>
    /// Класс, реализующий паттерн Одиночка.
    /// </summary>
    /// <remarks>
    /// Порождающий паттерн, гарантирующий, что для класса будет создан только один единственный экземпляр.
    /// </remarks>
    public class Singleton
    {
        /// <summary>
        /// Объект синхронизации, необходим для безопасности при многопоточном использовании.
        /// </summary>
        private static object _sync = new object();

        /// <summary>
        /// Основной объект, в котором будет храниться уникальный экземпляр класса. 
        /// </summary>
        private static volatile Singleton _instance;

        /// <summary>
        /// Какие-либо хранимые данные.
        /// </summary>
        private string _data;

        /// <summary>
        /// Данные, используемые в классе.
        /// </summary>
        public string Data => _data;

        /// <summary>
        /// Защищенный конструктор для инициализации единственного экземпляра класса.
        /// </summary>
        /// <param name="data">Данные, используемые в классе.</param>
        private Singleton(string data)
        {
            _data = data;
        }

        /// <summary>
        /// Получить экземпляр класса.
        /// </summary>
        /// <param name="data">Инициализирующие данные класса.</param>
        /// <returns>Уникальный экземпляр класса.</returns>
        public static Singleton GetInstance(string data)
        {
            if(string.IsNullOrEmpty(data))
            {
                throw new ArgumentNullException(nameof(data));
            }

            // Если экземпляр еще не инициализирован - выполняем инициализацию. 
            // Иначе возвращаем имеющийся экземпляр.
            if (_instance == null)
            {
                lock (_sync) // Используется чтобы избежать одновременного доступа критической секции из разных потоков.
                {
                    if (_instance == null)
                    {
                        _instance = new Singleton(data);
                    }
                }
            }

            return _instance;
        }

        /// <summary>
        /// Приведение объекта к строке.
        /// </summary>
        /// <returns>Данные класса в строковом формате.</returns>
        public override string ToString()
        {
            return Data;
        }
    }
}

Вызов класса. Попытаемся создать несколько экземпляров класса одиночки.

Program.cs
static void Main(string[] args)
{
  var singleton = Singleton.GetInstance("Привет, мир!");
  var singleton2 = Singleton.GetInstance("Здравствуй, космос!");
  Console.WriteLine(singleton2.Data);
  Console.ReadLine();
}

В результате получим следующий результат:

Singleton result
Singleton result

Как мы видим, при попытке создания нескольких экземпляров класса, мы получаем один единственный уникальный экземпляр класса, с единой точкой входа и реализующий механизм поздней инициализации. Таким образом, мы полностью добились желаемого результата. Исходный код программы доступен по ссылке в репозитории https://github.com/shwanoff/Singleton

Советую также изучить статью Паттерн проектирования Стратегия (Strategy) на языке C#. А еще подписывайтесь на группу ВКонтакте, Telegram и YouTube-канал. Там еще больше полезного и интересного для программистов.