Паттерны проектирования в .NET за 5 минут — Singleton(Одиночка)

Привет. Это второй урок из курса «Паттерны за 5 минут» и в данном уроке мы поговорим о паттерне «Singleton».

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

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

На платформе .NET реализация данного паттерна должна отвечать двум основным требованиям:

  • Доступ к singleton в многопоточной среде, так как на практике однопоточные .NET приложениями практически не встречаются.
  • Обеспечение «ленивости» создания singleton, когда его инициализация происходит непосредственно после того, как поступил запрос на использование данного класса. Но при этом инициализация будет происходить только один раз.

Реализуем вариант singleton, используя Lazy<T>

sealed class Logger
{
    private static readonly Lazy<Logger> _instance = new Lazy<Logger>(()=> new Logger(String.Empty));
    private readonly StringBuilder _stringLogs;

    public Logger()
    {
    }

    private Logger(string text)
    {
        _stringLogs = new StringBuilder(text);
    }

    private static Logger Instance => _instance.Value;

    public void Write(string logs)
    {
        Instance._stringLogs.AppendLine(logs);
    }

    public string Read()
    {
        return Instance._stringLogs.ToString();
    }
}

Перейдём к варианту реализации с использованием внутреннего класса. Создадим класс LoggerNestedClass. В нём объявим переменную типа StringBuilder для хранения данных логов. Добавим конструктор без параметров и с параметром, по аналогии, как мы это делали ранее. Затем добавим внутренний класс InstanceHolder. В нём добавим статический конструктор без параметров. И статическую переменную Instance, типа LoggerInstanceClass. Далее добавляем методы Write и Read также аналогичные тем, что добавлялись ранее. Здесь стоит заметить, что именно использование внутреннего класса, делает эту реализацию ленивой.

class LoggerNestedClass
{
    private readonly StringBuilder _stringLogs;

    public LoggerNestedClass()
    {
    }

    private LoggerNestedClass(string text)
    {
        _stringLogs = new StringBuilder(text);
    }

    private class InstanceHolder
    {
        static InstanceHolder()
        {
        }
        internal static readonly LoggerNestedClass Instance = new LoggerNestedClass(String.Empty);
    }

    private static LoggerNestedClass Instance
    {
        get { return InstanceHolder.Instance; }
    }

    public void Write(string logs)
    {
        Instance._stringLogs.AppendLine(logs);
    }

    public string Read()
    {
        return Instance._stringLogs.ToString();
    }
}

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

static class LoggerStaticClass
{
    private static readonly StringBuilder StringLogs = new StringBuilder();

    public static void Write(string logs)
    {
        StringLogs.AppendLine(logs);
    }

    public static string Read()
    {
        return StringLogs.ToString();
    }
}

Singleton – это определённо самый критикуемый паттерн. Многие называют его антипаттерном из-за того, что он вводит глобальные переменные, вместе со всеми их недостатками. Если вы выбираете между использованием синглтона и статического класса, помните, что синглтон имеет куда большую гибкость, но в противовес статические классы обычно легче использовать.

Приятного программирования.

Добавить комментарий