All4smoke.ru

Про наркотики и зависимость
0 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

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

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

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

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

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

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

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

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

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

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

Singleton (Одиночка)

Singleton (Одиночка)

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

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

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

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

Singleton.cs

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

Program.cs

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

Singleton result

Singleton result

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

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

Продукция бренда «Синглтон»

Рассмотрим несколько видов продукции:

Состав виски синглтон

  • The Singleton of Dufftown Tailfire. Виски singleton tailfire является самым распространённым в России. Напиток с ярко выраженным вкусом в котором прослеживаются нотки фруктового сиропа, кокоса и шоколада.
  • The Singleton of Dufftown 12 Years Old (виски синглтон 12 лет). Выдержанный не менее 12 ти лет односолодовый виски, частый гость на полках отечественных магазинов. Во вкусе напитка чётко отслеживаются ноты кофе, орехов и фруктов.
  • The Singleton of Dufftown 15 Years Old. Старший брат предыдущего виски, выдержанный в бочках из европейского дуба не менее 15 ти лет. Длительная выдержка привнесла во вкус напитка нотки карамели, мёда и зелёных яблок. В послевкусии напитка замечены нотки специй и бисквита.
  • The Singleton of Dufftown 18 Years Old. Виски с выдержкой более 18 ти лет, за счёт чего в напитке угадываются нотки кокоса и мяты. Неповторимый долгий вкус для настоящих ценителей крепких напитков.
  • The Singleton of Dufftown Sunray. Похожий на The Singleton of Dufftown Tailfire, виски синглтон санрей отличается нотками мёда и ванили, которые достигаются благодаря сырью, которое используется в производстве виски Singleton of Dufftown Sunray.
Читайте так же:
Обзор виски Glenfiddich (Гленфиддик)

Реализация паттерна Singleton

Классическая реализация Singleton

Рассмотрим наиболее часто встречающуюся реализацию паттерна Singleton.

Клиенты запрашивают единственный объект класса через статическую функцию-член getInstance() , которая при первом запросе динамически выделяет память под этот объект и затем возвращает указатель на этот участок памяти. Впоследcтвии клиенты должны сами позаботиться об освобождении памяти при помощи оператора delete .

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

Singleton Мэйерса

Внутри getInstance() используется статический экземпляр нужного класса. Стандарт языка программирования C++ гарантирует автоматическое уничтожение статических объектов при завершении программы. Досрочного уничтожения и не требуется, так как объекты Singleton обычно являются долгоживущими объектами. Статическая функция-член getInstance() возвращает не указатель, а ссылку на этот объект, тем самым, затрудняя возможность ошибочного освобождения памяти клиентами.

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

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

Улучшенная версия классической реализации Singleton

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

Ключевой особенностью этой реализации является наличие класса SingletonDestroyer , предназначенного для автоматического разрушения объекта Singleton. Класс Singleton имеет статический член SingletonDestroyer , который инициализируется при первом вызове Singleton::getInstance() создаваемым объектом Singleton . При завершении программы этот объект будет автоматически разрушен деструктором SingletonDestroyer (для этого SingletonDestroyer объявлен другом класса Singleton ).

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

Использование нескольких взаимозависимых одиночек

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

  • Как гарантировать, что к моменту использования одного одиночки, экземпляр другого зависимого уже создан?
  • Как обеспечить возможность безопасного использования одного одиночки другим при завершении программы? Другими словами, как гарантировать, что в момент разрушения первого одиночки в его деструкторе еще возможно использование второго зависимого одиночки (то есть второй одиночка к этому моменту еще не разрушен)?
Читайте так же:
Виски Spicebox (Спайсбокс) и его особенности

Управлять порядком создания одиночек относительно просто. Следующий код демонстрирует один из возможных методов.

Объект Singleton1 гарантированно инициализируется раньше объекта Singleton2 , так как в момент создания объекта Singleton2 происходит вызов Singleton1::getInstance() .

Гораздо сложнее управлять временем жизни одиночек. Существует несколько способов это сделать, каждый из них обладает своими достоинствами и недостатками и заслуживают отдельного рассмотрения. Обсуждение этой непростой темы остается за рамками проекта. Подробную информацию можно найти в [3].

Несмотря на кажущуюся простоту паттерна Singleton (используется всего один класс), его реализация не является тривиальной.

Подводные камни Singleton: почему самый известный шаблон проектирования нужно использовать с осторожностью

Обложка: Подводные камни Singleton: почему самый известный шаблон проектирования нужно использовать с осторожностью

Паттерн «Одиночка» — пожалуй, самый известный паттерн проектирования. Тем не менее, он не лишен недостатков, поэтому некоторые программисты (например, Егор Бугаенко) считают его антипаттерном. Разбираемся в том, какие же подводные камни таятся в Singleton’е.

Определение паттерна

Само описание паттерна достаточно простое — класс должен гарантированно иметь лишь один объект, и к этому объекту должен быть предоставлен глобальный доступ. Скорее всего, причина его популярности как раз и кроется в этой простоте — всего лишь один класс, ничего сложного. Это, наверное, самый простой для изучения и реализации паттерн. Если вы встретите человека, который только что узнал о существовании паттернов проектирования, можете быть уверены, что он уже знает про Singleton. Проблема заключается в том, что когда из инструментов у вас есть только молоток, всё вокруг выглядит как гвозди. Из-за этого «Одиночкой» часто злоупотребляют.

Простейшая реализация

Как уже говорилось выше, в этом нет ничего сложного:

  • Сделайте конструктор класса приватным, чтобы не было возможности создать экземпляр класса извне.
  • Храните экземпляр класса в private static поле.
  • Предоставьте метод, который будет давать доступ к этому объекту.

Принцип единственной обязанности

В объектно-ориентированном программировании существует правило хорошего тона — «Принцип едиственной обязанности» (Single Responsibility Principle, первая буква в аббревиатуре SOLID). Согласно этому правилу, каждый класс должен отвечать лишь за один какой-то аспект. Совершенно очевидно, что любой Singleton-класс отвечает сразу за две вещи: за то, что класс имеет лишь один объект, и за реализацию того, для чего этот класс вообще был создан.

Принцип единственной обязанности был создан не просто так — если класс отвечает за несколько действий, то, внося изменения в один аспект поведения класса, можно затронуть и другой, что может сильно усложнить разработку. Так же разработку усложняет тот факт, что переиспользование (reusability) класса практически невозможно. Поэтому хорошим шагом было бы, во-первых, вынести отслеживание того, является ли экземпляр класса единственным, из класса куда-либо во вне, а во-вторых, сделать так, чтобы у класса, в зависимости от контекста, появилась возможность перестать быть Singleton’ом, что позволило бы использовать его в разных ситуациях, в зависимости от необходимости (т.е. с одним экземпляром, с неограниченным количество экземпляров, с ограниченным набором экземпляров и так далее).

Читайте так же:
Обзор виски John Corr (Джон Корр)

Тестирование

Один из главных минусов паттерна «Одиночка» — он сильно затрудняет юнит-тестирование. «Одиночка» привносит в программу глобальное состояние, поэтому вы не можете просто взять и изолировать классы, которые полагаются на Singleton. Поэтому, если вы хотите протестировать какой-то класс, то вы обязаны вместе с ним тестировать и Singleton, но это ещё полбеды. Состояние «Одиночки» может меняться, что порождает следующие проблемы:

  • Порядок тестов теперь имеет значение;
  • Тесты могут иметь нежелательные сторонние эффекты, порождённые Singleton’ом;
  • Вы не можете запускать несколько тестов параллельно;
  • Несколько вызовов одного и того же теста могут приводить к разным результатам.

На эту тему есть отличный доклад с «Google Tech Talks»:

Скрытые зависимости

Обычно, если классу нужно что-то для работы, это сразу понятно из его методов и конструкторов. Когда очевидно, какие зависимости есть у класса, гораздо проще их предоставить. Более того, в таком случае вы можете использовать вместо реально необходимых зависимостей заглушки для тестирования. Если же класс использует Singleton, это может быть совершенно не очевидно. Всё становится гораздо хуже, если экземпляру класса для работы необходима определённая инициализация (например, вызов метода init(. ) или вроде того). Ещё хуже, если у вас существует несколько Singleton’ов, которые должны быть созданы и инициализированы в определённом порядке.

Загрузчик класса

Если говорить о Java, то обеспечение существования лишь одного экземпляра класса, которое так необходимо для Singleton, становится всё сложнее. Проблема в том, что классическая реализация не проверяет, существует ли один экземпляр на JVM, он лишь удостоверяется, что существует один экземпляр на classloader. Если вы пишете небольшое клиентское приложение, в котором используется лишь один classloader, то никаких проблем не возникнет. Однако если вы используете несколько загрузчиков класса или ваше приложение должно работать на сервере (где может быть запущено несколько экземпляров приложения в разных загрузчиках классов), то всё становится очень печально.

Десериализация

Ещё один интересный момент заключается в том, что на самом деле стандартная реализация Singleton не запрещает создавать новые объекты. Она запрещает создавать новые объекты через конструктор. А ведь существуют и другие способы создать экземпляр класса, и один из них — сериализация и десериализация. Полной защиты от намеренного создания второго экземпляра Singleton’а можно добиться только с помощью использования enum’а с единственным состоянием, но это — неоправданное злоупотребление возможностями языка, ведь очевидно, что enum был придуман не для этого.

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

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

Однако здесь начинаются проблемы с потоками, которые могут создавать несколько различных объектов. Происходит это примерно так:

  • Первый поток обращается к getInstance() , когда объект ещё не создан;
  • В это время второй тоже обращается к этому методу, пока первый ещё не успел создать объект, и сам создаёт его;
  • Первый поток создаёт ещё один, второй, экземпляр класса.
Читайте так же:
Обзор виски Lagavulin 16 years old (Лагавулин 16 лет выдержки)

Разумеется, можно просто пометить метод как synchronised , и эта проблема исчезнет. Проблема заключается в том, что, сохраняя время на старте программы, мы теперь будем терять его каждый раз при обращении к Singleton’у из-за того, что метод синхронизирован, а это очень дорого, если к экземпляру приходится часто обращаться. А ведь единственный раз, когда свойство synchronised действительно требуется — первое обращение к методу.

Есть два способа решить эту проблему. Первый — пометить как synchronised не весь метод, а только блок, где создаётся объект:

Не забывайте, что это нельзя использовать в версии Java ниже, чем 1.5, потому что там используется иная модель памяти. Также не забудьте пометить поле instance как volatile .

Sportmaster Lab , Санкт-Петербург, Москва, Липецк, можно удалённо , По итогам собеседования

Второй путь — использовать паттерн «Lazy Initialization Holder». Это решение основано на том, что вложенные классы не инициализируются до первого их использования (как раз то, что нам нужно):

Рефлексия

Мы запрещаем создавать несколько экземпляров класса, помечая конструктор приватным. Тем не менее, используя рефлексию, можно без особого труда изменить видимость конструктора с private на public прямо во время исполнения:

Конечно, если вы используете Singleton только в своём приложении, переживать не о чем. А вот если вы разрабатываете модуль, который затем будет использоваться в сторонних приложениях, то из-за этого могут возникнуть проблемы. Какие именно, зависит от того, что делает ваш «Одиночка» — это могут быть как и риски, связанные с безопасностью, так и просто непредсказуемое поведение модуля.

Заключение

Несмотря на то, что паттерн Singleton очень известный и популярный, у него есть множество серьёзных недостатков. Чем дальше, тем больше этих недостатков выявляется, и оригинальные паттерны из книги GOF «Design Patterns» часто сегодня считаются антипаттернами. Тем не менее, сама идея иметь лишь один объект на класс по-прежнему имеет смысл, но достаточно сложно реализовать ее правильно.


Где-то в параллельной вселенной Гонконга люди ютятся на паре квадратов за $500 тыс.

Гонконг смело можно назвать городом контрастов. За крохотную нано-квартиру, которую у нас чаще всего называют «смарт», здесь просят до полумиллиона долларов.

Чтобы понимали, площадь такой каморки не превышает 10-12 квадратов. У миллионеров же счет идет на сотни метров.

The Solution

To be able to use scoped services within a singleton, you must create a scope manually. A new scope can be created by injecting an IServiceScopeFactory into your singleton service (the IServiceScopeFactory is itself a singleton, which is why this works). The IServiceScopeFactory has a CreateScope method, which is used for creating new scope instances.

The created scope has it’s own IServiceProvider , which you can access to resolve your scoped services.

It is important to make sure that the scope only exists for as long as is necessary, and that it is properly disposed of once you have finished with it. This is to avoid any issues of captive dependencies (as discussed at the start of this article. Therefore, I would recommend:

  • Only define the scope within the method that you intend to use it. It might be tempting to assign it to a field for reuse elsewhere in the singleton service, but again this will lead to captive dependencies.
  • Wrap the scope in a using statement. This will ensure that the scope is properly disposed of once you have finished with it.
Читайте так же:
Обзор канадских марок виски

Виски Diageo

Скотч, которым сегодня владеет компания, имеет богатую историю, начиная от гениальности Джона Уокера в смешивании виски до решимости Элизабет Камминг предложить односолодовый Cardhu. Сегодня Диагео производит более 100 отдельных марок виски. Одной из лидирующих категорий является, конечно же шотландский скотч.

Ведущим брендом так и остается Johnnie Walker. Однако компания может похвастаться и другими премиальными марками, включая Talisker, Mortlach и The Singleton. Среди других дочерних компаний значатся Auchroisk, Blair Athol, Clynelish, Caol Ila, Cragganmore, Dailuaine, Dalwhinnie, Glen Elgin, Glen Spey, Glenkinchie, Linkwood, Mannochmore, Mortlach, Strathmill и Teaninich. Diageo также унаследовал богатое наследие бренда Justerini & Brooks. Самая ранняя «компания-прародительница» известна за шотландский виски J&B. Она была основана в 1749 году.

В 2018 году появились новости о том, что некоторые из напитков, выпущенных на российском рынке, будут разливаться у нас. Это Bell’s, White Horse и Black & White. Однако производство все еще останется в Шотландии. Изменения касаются именно «сортировки» по бутылкам. В этом же году вышла специальная коллекция Special Releases. Она включает в себя некоторые из самых редких и старых виски со знаменитых и даже уже закрытых заводов. Среди них 14-летний The Singleton Of Glen Ord и Inchgower:

«Запуск Специального Релиза – основной момент в году для нас и любителей виски. Это коллекция, которая позволяет как опытным ценителям, так и новичкам выискивать действительно уникальные ограниченные выпуски. Исключительное разнообразие и качество виски не разочарует, и мы с нетерпением ждем возможности поделиться всеми историями о них», – заявили в компании.

Компания также является участником Международного Дня Скотча. Празднования проходят в домах, барах, клубах и других местах по всему миру. Это не только возможность выпить любимое виски, но и пролить свет на историю напитка. Диагео предлагают уникальную возможность посетить их архив в Менстри, недалеко от города Аллоа. Это самая большая коллекция напитков из более чем 10 000 бутылок.

Помимо шотландского скотча Диагео выпускает американский Seagram’s Seven Crown, канадский Crown Royal, George Dickel из Теннесси, ирландский Roe & Co. Что касается других напитков, то среди обширного ассортимента можно выделить раки (ракы), популярный в Турции, Греции и на Балканах. Представлены марки Yeni Rakı, Tekirdağ Rakısı, Kulüp Rakı и Altınbaş. Также есть шнапс Rumple Minze и Archers, китайский напиток Байцзю под брендами Shui Jing Fang и Nếp Mới и готовые коктейли Pimm’s, Jeremiah Weed и Smirnoff.

голоса
Рейтинг статьи
Ссылка на основную публикацию
Adblock
detector