Singleton class in swift

Введение

Синглтон (тип, допускающий создание только одного своего экземпляра в каждом домене приложения) – очень полезный шаблон, находящий своей применение  почти в каждом большом проекте – особенно если это многопоточное многопользовательское приложение. Создавать и применять синглтоны в веб-среде при разработке веб-приложений на C# в каркасе ASP.NET не всегда просто.

В веб-приложениях используются три вида синглтонов:•    Один экземпляр на каждый веб-запрос•    Один экземпляр на каждого пользователя (сессию)•    Один экземпляр на все веб-приложение

Первые два случая не являются проблемой; один экземпляр на каждый веб-запрос часто нельзя назвать синглтоном, но это зависит от сложности веб-запроса. Наиболее интересен третий пункт “Один экземпляр на все веб-приложение”, являющийся трудным.

Обычно стандартный шаблон синглтон применяется, когда по умолчанию есть только один рабочий процесс. Проблема заключается в том, как реализовать синглтон в среде с множеством рабочих потоков. При наличии единственного рабочего потока реализовать синглтон просто, так как все веб-запросы будут совместно использовать его статический экземпляр по всему рабочему потоку; единственная проблема – как защитить его, поскольку среда все же многопоточная, и во избежание неприятностей можно добавить «lock», как в примере ниже.

public class Singleton {    static Singleton instance = null;    static readonly object padlock = new object();    Singleton() { }    public static Singleton Instance {        get {            lock (padlock) {                if (instance == null) {                    instance = new Singleton();                }                return instance;            }        }    }}

Это будет хорошо работать при использовании одного рабочего потока в пуле приложений, но в наши дни большинство текущих серверных платформ имеют многоядерные процессоры, и при одном рабочем потоке они не используются по максимуму. Другой минус наличия одного рабочего потока заключается в том, что когда один из запросов работает медленно, от этого непосредственно пострадают все прочие запросы. Чтобы масштабировать производительность, надо повысить количество рабочих потоков. Но, к сожалению, статические переменные совместно используются только через один рабочий поток, следовательно, получилось бы много синглтонов, один на каждый поток – что иногда допустимо, но часто становится серьезной проблемой (например, могут быть нарушения доступа) и, в конце концов, это уже не соблюдает шаблон синглтон.

Исторические факты

Впервые Синглтон появился на рынке в 1974 году. Он был выпущен известной шотландской винокурней Auchroisk Distillery. При этом изначально продукт производился под торговой маркой Justerini & Brooks, и его изготавливали исключительно для торговли в Duty Free. Заметив, насколько популярным стал этот скотч, учредители компании Dufftown Distillery приняли решение о расширении производственного процесса и создании новой торговой марки Синглтон из Ортруска, говорящей о том, что это оригинальный напиток из весьма известного региона.

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

Lease

A lease can be used as an additional safety measure to ensure that two singletons don’t run at the same time. Reasons for how this can happen:

  • Network partitions without an appropriate downing provider
  • Mistakes in the deployment process leading to two separate Akka Clusters
  • Timing issues between removing members from the Cluster on one side of a network partition and shutting them down on the other side

A lease can be a final backup that means that the singleton actor won’t be created unless the lease can be acquired.

To use a lease for singleton set to the configuration location of the lease to use. A lease with with the name is used and the owner is set to the .

If the cluster singleton manager can’t acquire the lease it will keep retrying while it is the oldest node in the cluster. If the lease is lost then the singleton actor will be terminated then the lease will be re-tried.

Singleton-объекты и scoped-сервисы

Последнее обновление: 07.11.2019

Все объекты, которые используются в ASP.NET Core, имеет три варианта жизненного цикла. Singleton-объекты создаются один раз при запуске приложения,
и при всех запросах к приложению оно использует один и тот же singleton-объект. К подобным singleton-объектам относятся, к примеру, компоненты
middleware или сервисы, которые регистрируются с помощью метода .

Transient-объекты создаются каждый раз, когда нам требуется экземпляр определенного класса. А scoped-объекты создаются по одному на каждый запрос.

Одни объекты или сервисы с помощью встроенного механизма dependency injection можно передать в другие объекты.
Наиболее распространенный способ внедрения объектов предсталяет инъекция через конструктор. Однако начиная с версии ASP.NET Core 2.0
мы не можем передавать scoped-сервисы в конструктор singleton-объектов.

Например, пусть будут опеделены следующие классы:

public interface ITimer
{
	string Time { get; }
}
public class Timer : ITimer
{
	public Timer()
	{
		Time = System.DateTime.Now.ToString("hh:mm:ss");
	}
	public string Time { get; }
}
public class TimeService
{
	private ITimer _timer;
	public TimeService(ITimer timer)
	{
		_timer = timer;
	}
	public string GetTime()
	{
		return _timer.Time;
	}
}

TimeService получает через конструктор сервис ITimer и использует его для получения текущего времени.

Также пусть будет определен компонент middleware TimerMiddleware:

public class TimerMiddleware
{
	private readonly RequestDelegate _next;
	TimeService _timeService;
	public TimerMiddleware(RequestDelegate next, TimeService timeService)
	{
		_next = next;
		_timeService = timeService;
	}

	public async Task Invoke(HttpContext context)
    {
		context.Response.ContentType = "text/html; charset=utf-8";
		await context.Response.WriteAsync($"Текущее время: {_timeService?.GetTime()}");
	}
}

Компонент TimerMiddleware получает сервис TimeService и отправляет в ответ клиенту информацию о текущем времени.

TimerMiddleware является singleton-объектом. И теперь в классе Startup зарегистрируем сервис TimeService как scoped-объект:

public class Startup
{
	public void ConfigureServices(IServiceCollection services)
	{
		services.AddTransient();
		services.AddScoped();
	}
	public void Configure(IApplicationBuilder app)
	{
		app.UseDeveloperExceptionPage();
        app.UseMiddleware();
	}
}

Если мы запустим приложение, то мы столкнемся с ошибкой типа «InvalidOperationException: Cannot resolve scoped service ‘DIApp.TimeService’ from root provider.»:

То есть на момент создания объекта TimerMiddleware scoped-сервис TimeService еще не установлен, соответственно он использоваться не может. А без создания объекта TimeService
нельзя создать объект TimerMiddleware.

Аналогичная ситуация может возникнуть, если TimeService добавляется как Transient, а сервис ITimer определен как Scoped:

public void ConfigureServices(IServiceCollection services)
{
	services.AddScoped();
	services.AddTransient();
}

В этом случае для создания объекта TimeService надо получить сервис ITimer, но на момент вызова конструктора TimerMiddleware сервис ITimer еще неопределен:

Для выхода из этой ситуации ни TimeService, ни ITimer не должны иметь жизненный цикл Scoped. То есть это может быть Transient или Singleton.

Рассмотрим еще одну ситуацию, с которой можно столкнуться в любой части приложения, а не только в конструкторе middleware, когда
сервис TimeService представляет singleton, а ITimer — scoped-объект:

public void ConfigureServices(IServiceCollection services)
{
	services.AddScoped();
	services.AddSingleton();
}

И, допустим, эти сервисы используются в TimerMiddleware непосредственно при обработке запроса в методе Invoke/InvokeAsync:

public class TimerMiddleware
{
	private readonly RequestDelegate _next;
	public TimerMiddleware(RequestDelegate next)
	{
		_next = next;
	}

	public async Task InvokeAsync(HttpContext context, TimeService timeService)
	{
		context.Response.ContentType = "text/html; charset=utf-8";
		await context.Response.WriteAsync($"Текущее время: {timeService?.GetTime()}");
	}
}

При запуске приложения мы опять же столкнемся с ошибкой, только немного другой «Cannot consume scoped service ‘DIApp.ITimer’ from singleton ‘DIApp.TimeService'»

Но суть будет та же самая — мы не можем по умолчанию передавать в конструктор singleton-объекта scoped-сервис.

НазадВперед

Какие бывают виды этого напитка

Выбирая односолодовые и —купажированный виски — известной торговой марки Синглтон, вы должны быть готовы к знакомству со внушительным многообразием алкоголя. Сегодня под популярным брендом выпускаются самые различные скотчи со своими интересными, а порой и завораживающими особенностями. К наиболее популярным продуктам, которые с легкостью можно встретить на полках каждого магазина Европы, следует отнести:

  • Виски Синглтон Спей Каскейд Односолод. Продукт янтарно-золотого цвета, с выразительным ароматом фруктов и сбалансированным вкусом, в котором прослеживаются нотки ванили и сладкого солода.
  • Виски Singleton 12 лет выдержки. Выдерживается в течение 12 лет в деревянных бочках из европейского и американского дуба. В цвете напитка преобладают золотые оттенки, а в аромате прослеживаются гладкие нотки сладкого солода. Вкус при этом сбалансирован и мягок. Он сопровождается ореховыми оттенками.
  • Singleton of Dufftown 15 Years Old. Обладает янтарным окрасом и ароматом с тонкими нотками фруктов. Выразительных нот во вкусе добавляет ирис и ореховые оттенки.
  • Виски Singleton of Dufftown 18 лет выдержки. У бленда глубокий янтарный окрас и характерный сладкий вкус с небольшой сухостью и оттенками мяты, яблочного сока и какао. В аромате слышен тон миндального печенья.
  • Sunray of Dufftown. Отличительной особенностью бленда является золотисто-желтый цвет и яркие оттенки ванили с медом в аромате. Вкус радует сливочными полутонами с характерным присутствием корицы и печеных яблок.
  • Tailfire of Dufftown. Односолодовый скот с ароматом яблока, малины, клюквы и табака. Во вкусовых оттенках прослеживаются шоколад, кедр, дубовая горечь и небольшая дымка.

Знаете ли вы? В 2014 году Singleton Tailfire of Dufftown занял почетное первое место на конкурсе «International Spirits Challenge».

Дегустационные характеристики

Каждый бленд коллекции именитого производителя, включая виски Синглтон 12 лет выдержки, представляет собой настоящий шедевр, полученный благодаря нетривиальной рецептуре. Молты изготавливаются путем отбора наиболее качественных ингредиентов. Зерно для —торфяного виски — сушится в специальных условиях. Далее в подготовленный солод добавляется мягкая вода, которую добывают из источника Dorie’s Wel, известного не только в Шотландии, но и во всем мире.

Брожение же каждого скотча из линейки производителя осуществляется в течение 120 часов. Именно это количество времени необходимо для того, чтобы продукт обрел свою неповторимую харизму

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

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

Цвет

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

Аромат

Запахи каждого бленда от Синглтон радуют изобилием фруктовых нот. Также характерной для них чертой являются ореховые полутона, сопровождающиеся фирменной дымкой Спейсайда.

Вкус

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

Код

Simple Solution

Не ленивая инициализация.

Lazy Initialization

Ленивая инициализация.

Не потокобезопасно

Synchronized Accessor

  • Ленивая инициализация.

  • Потокобезопасность

Низкая производительность в многопоточной среде

Double Checked Locking

  • Ленивая инициализация.

  • Потокобезопасность

  • Высокая производительность в многопоточной среде

Не поддерживается на ранних версиях Java. Ниже 1.5

Class Holder Singleton

  • Ленивая инициализация.

  • Потокобезопасность.

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

Для корректной работы необходима гарантия, что объект класса Singleton инициализируется без ошибок. Иначе первый вызов метода getInstance закончится ошибкой ExceptionInInitializerError, а все последующие NoClassDefFoundError.

Сравнительная таблица различных реализаций паттерна Singleton:

Реализация Ленивая инициализация Потокобезопасность Скорость работы при многопоточности Когда использовать?
+ Быстро Никогда. Либо когда не важна ленивая инициализация. Но лучше никогда.
+ Неприменимо Всегда, когда не нужна многопоточность
+ + Медленно Никогда. Либо когда скорость работы при многопоточности не имеет значения. Но лучше никогда
+ + Быстро В редких случаях, когда нужно обрабатывать исключения при создании синглтона. (когда неприменим Class Holder Singleton)
+ + Быстро Всегда, когда нужна многопоточность и есть гарантия, что объект синглтон класса будет создан без проблем.

Implementation

To begin with, let’s consider a simple singleton with draconian synchronization:

public class DraconianSingleton {
    private static DraconianSingleton instance;
    public static synchronized DraconianSingleton getInstance() {
        if (instance == null) {
            instance = new DraconianSingleton();
        }
        return instance;
    }

    // private constructor and other methods ...
}

Despite this class being thread-safe, we can see that there’s a clear performance drawback: each time we want to get the instance of our singleton, we need to acquire a potentially unnecessary lock.

To fix that, we could instead start by verifying if we need to create the object in the first place and only in that case we would acquire the lock.

Going further, we want to perform the same check again as soon as we enter the synchronized block, in order to keep the operation atomic:

public class DclSingleton {
    private static volatile DclSingleton instance;
    public static DclSingleton getInstance() {
        if (instance == null) {
            synchronized (DclSingleton .class) {
                if (instance == null) {
                    instance = new DclSingleton();
                }
            }
        }
        return instance;
    }

    // private constructor and other methods...
}

One thing to keep in mind with this pattern is that the field needs to be volatile to prevent cache incoherence issues. In fact, the Java memory model allows the publication of partially initialized objects and this may lead in turn to subtle bugs.

Синглтон дистанционной связи .NET

Ярким примером синглтона, для которого несколько рабочих потоков создают проблемы, является класс «Регистратор файла» – когда каждый запрос записывает некоторые данные в один и тот же файл. Вообразите, что произошло бы, если бы два синглтона одновременно попытались писать в один и тот же файл.

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

Идея в том, что канал HTTP-сервера синглтона будет создан при первой попытке  вызвать метод GetInstance(), стало быть, имеется полное отложенное создание экземпляра (как в первом примере кода). Если канал сервера уже создан (что можно выяснить путем захвата исключения сокета: второй блок кода), рабочий поток попытается подключиться к нему, чтобы создать посредника, позволяющего обращаться к синглтону.

try {     channel = new HttpChannel(8089);    ChannelServices.RegisterChannel(channel, false);    RemotingConfiguration.RegisterWellKnownServiceType(typeof(Singleton),                 "Singleton", WellKnownObjectMode.Singleton);    instance = (Singleton)Activator.GetObject(typeof(Singleton),             "http://localhost:8089/Singleton"); } catch (SocketException) {    channel = new HttpChannel();    ChannelServices.RegisterChannel(channel, false);    instance = (Singleton)Activator.GetObject(typeof(Singleton),            "http://localhost:8089/Singleton"); }

В этом примере используются жестко закодированные значения для номера порта и имени службы – однако лучше хранить их в Web.Config.

Итак, если на сервере уже был создан экземпляр канала сервера на конкретном номере порта, то new HttpChannel(8089) сгенерирует исключение сокета, следовательно, пропустите часть создания сервера и попытайтесь получить текущий экземпляр от существующего канала.

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

for (int i = 0; i     try {        instance = (Singleton)Activator.GetObject(typeof(Singleton),                     "http://localhost:8089/Singleton");         break;    } catch (RemotingException) {        Thread.Sleep(300);    }}

Другая проблема – что делать, когда главный рабочий поток (создавший синглтон и канал сервера) исчезает?

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

Чтобы решить эту проблему, надо проверять действительность подключения и объекта перед возвратом экземпляра вызывающей функции GetInstance(). Для достижения этого создается пустой метод CheckConnection() (пример ниже), возвращающий логическое значение. Если этот метод генерирует исключение сокета, то ссылка больше недействительна.

public bool CheckConnection() {    return true; }

Если стало известно, что подключение к серверу синглтона исчезло, то создается новое подключение в текущем рабочем потоке.

Как правильно подавать и пить?

Традиции употребления виски зависят от определенной страны: кто-то пьет напиток в чистом виде, кто-то добавляет лед, колу или содовую. Но все же существуют классические правила употребления Синглтона:

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

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

  3. Идеальная температура подачи – 20 градусов.
  4. Дегустация. Употребляется алкоголь мелкими глотками, оценивая в начале аромат напитка. Сразу виски не проглатывают, что усиливает его послевкусие.

Configuration

The following configuration properties are read by the when created with a parameter. It is also possible to amend the or create it from another config section with the same layout as below. is a parameter to the factory method, i.e. each singleton can be configured with different settings if needed.

The following configuration properties are read by the when created with a parameter. It is also possible to amend the or create it from another config section with the same layout as below. is a parameter to the factory method, i.e. each singleton proxy can be configured with different settings if needed.

Плюсы и минусы паттерна Singleton

  1. Дает гарантию, что у класса будет всего один экземпляр класса.

  2. Предоставляет глобальную точку доступа к экземпляру данного класса.

  1. Синглтон нарушает SRP (Single Responsibility Principle) — класс синглтона, помимо непосредственных обязанностей, занимается еще и контролированием количества своих экземпляров.

  2. Зависимость обычного класса или метода от синглтона не видна в публичном контракте класса.

  3. Глобальные переменные это плохо. Синглтон превращается в итоге в одну здоровенную глобальную переменную.

  4. Наличие синглтона снижает тестируемость приложения в целом и классов, которые используют синглтон, в частности.

Как и когда создавать Singleton?

Любой класс, которого Вам понадобится только один экземпляр можно сделать Singleton-ом. Вот подумайте — сколько памяти Вы сэкономите… Сколько времени… Ммм… Красота.

Должен ли Singleton быть доступен из любой точки программы?

В Интернете часто можно увидеть, что Singleton якобы должен быть доступен «из любой точки программы». Это не совсем так.  Singleton, как и любые другие данные, должны быть доступны только там, где они нужны. Если они нужны в части программы А — нет необходимости чтобы ее видели части В, C и D.

Тем не менее, обратите внимание на то, какая видимость должна быть у Вашего Singleton-а. Обратите внимание — нужно ли Вам работать с потоками

Обратите внимание — нужно ли Вам работать с потоками. В зависимости от того, работаете Вы с потоками или нет, можно будет выбрать оптимальную реализацию Singleton-а. 

В зависимости от того, работаете Вы с потоками или нет, можно будет выбрать оптимальную реализацию Singleton-а. 

Пример  — Самый простой вариант реализации

public class Singleton {
private Singleton(){…}
public static final Singleton INSTANCE = new Singleton();
}

1
2
3
4

publicclassSingleton{

privateSingleton(){…}

publicstaticfinalSingleton INSTANCE=newSingleton();

}

Это самый простой пример реализации Singleton.

Плюсы и минусы

Честных Singleton в Java не существует

Из-за reflection, нескольких ClassLoader-ов и других причин, нельзя создать настоящий-настоящий Singleton.

Трудности  при тестировании

Singleton трудно изолировать — это усложняет написание юнит-тестов.

Нарушает «Принцип единственной обязанности» класса

«Принцип едиственной обязанности» считается хорошим тоном в ООП. Он состоит в том, что класс должен отвечать только за что-то одно. Например, класс «КореньКвадратный» должен отвечать только за извлечения квадратного корня. Он не должен одновременно печь пирог и гладить кота — только извлекать квадратный корень. Класс Singleton, помимо своих прямых обязанностей, занимается контролем количества своих классов. Это считается плохим тоном.

Больше примеров реализации Sigleton-а (их много!) можно посмотреть в нашей следующей статье «Паттерны проектирования: Singleton — Часть 2»

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

Supervision

There are two actors that could potentially be supervised. For the singleton created above these would be:

  • Cluster singleton manager e.g. which runs on every node in the cluster
  • The user actor e.g. which the manager starts on the oldest node

The Cluster singleton manager actor should not have its supervision strategy changed as it should always be running. However it is sometimes useful to add supervision for the user actor. To accomplish this add a parent supervisor actor which will be used to create the ‘real’ singleton instance. Below is an example implementation (credit to this StackOverflow answer)

Scala
Java

And used here

Scala
Java

История и развитие бренда Singleton

Справка! Первая бутылка Синглтона была выпущена в 1974 году под брендом Justerini&Brooks. Изначально виски носил название «Синглтон из Ортруска». Ортруск – местность, где началось производство этого напитка.

Позже производитель, решив подчеркнуть эксклюзивность виски, оставил только первое слово названия.

Первая бутылка Синглтона разливалась на вискокурне, которая была построена в Спейсайде и называлась Auchroisk Distillery. Со временем для российского потребителя производит виски тэйлфайр вискокурня даффтаун.

И, если в начале напиток производился в небольших количествах, для удовлетворения потребностей беспошлинных магазинов Duty Free, то сегодня выпускается около 4 миллионов литров алкоголя в год. Это весьма значительный показатель для рынка этого сегмента.

Примечание. Интересно, что Синглтон может отличаться своим названием, но это никак не влияет на технологию производства и качество напитка. Название зависит от места производства и потенциального потребителя скотча. Так различают несколько названий:

  • Singleton of Dufftown – производится специально для российского и европейского потребления;
  • Singleton of Glen Ord – продается в азиатских странах;
  • Singleton of Glendullan – поставляется для американских потребителей.

Виды и стоимость виски Singleton

  • Sunray of Dufftown. Скотч золотисто-желтого цвета. Отличается сливочным вкусом с оттенками ванили и меда, привкусом печеных яблок с корицей. Стоимость: от 3 000 рублей за бутылку 0,7 литра.
  • Tailfire of Dufftown. Виски золотисто-янтарного цвета с ароматами яблока, клюквы, малины и табака. Во вкусе чувствуются оттенки шоколада, дуба, кедра и дыма. Стоимость: от 3 000 рублей.
  • Dufftown 12 Years Old. Виски выдержкой 12 лет. Имеет цвет красного дерева с золотистыми отблесками. Вкус гладкий, сбалансированный, со сладкими тонами и ореховым послевкусием. Стоимость в России: от 3 500 рублей.
  • Dufftown 15 Years Old. Скотч янтарного цвета выдержкой 15 лет. Отличается гладкой текстурой, с нотками ириса и фруктов. Послевкусие длительное, с ореховым оттенком. При добавлении небольшого количества воды появляются нотки пряностей. Стоимость: от 3 900 рублей.
  • Dufftown 18 Years Old. Это виски выдержкой 18 лет. Выделяется глубоким насыщенным янтарным цветом. Имеет сладкий сухой вкус с оттенками мяты, орехов, яблочного сока и полутонами какао и миндального печенья. Стоимость: от 6 500 рублей.
Ссылка на основную публикацию