Прославленная глобальная переменная - становится прославленным глобальным классом. Некоторые говорят, что нарушают объектно-ориентированный дизайн.
Дайте мне сценарии, кроме старого доброго логгера, где имеет смысл использовать синглтон.
Прославленная глобальная переменная - становится прославленным глобальным классом. Некоторые говорят, что нарушают объектно-ориентированный дизайн.
Дайте мне сценарии, кроме старого доброго логгера, где имеет смысл использовать синглтон.
Ответы:
В поисках истины я обнаружил, что на самом деле очень мало «приемлемых» причин использовать Синглтон.
Одна из причин, которая имеет тенденцию появляться снова и снова в интернете, - это класс «журналирования» (который вы упомянули). В этом случае Singleton может использоваться вместо одного экземпляра класса, потому что класс журналирования обычно должен использоваться снова и снова до тошноты каждым классом в проекте. Если каждый класс использует этот класс ведения журнала, внедрение зависимостей становится громоздким.
Ведение журнала - это конкретный пример «приемлемого» Singleton, потому что он не влияет на выполнение вашего кода. Отключите ведение журнала, выполнение кода остается прежним. Включите это, то же самое. Миско объясняет это следующим образом в « Коренной причине одиночек »: «Информация здесь течет одним путем: из вашего приложения в регистратор. Несмотря на то, что регистраторы находятся в глобальном состоянии, поскольку никакая информация не поступает из регистраторов в ваше приложение, регистраторы приемлемы».
Я уверен, что есть и другие веские причины. Алекс Миллер, в « Паттернах, которые я ненавижу », рассказывает о локаторах сервисов и пользовательском интерфейсе на стороне клиента, которые, возможно, являются «приемлемым» выбором.
Читайте больше в Синглтоне, я люблю тебя, но ты меня опускаешь.
Кандидат-одиночка должен соответствовать трем требованиям:
Если ваш предложенный Singleton имеет только одно или два из этих требований, редизайн почти всегда является правильным вариантом.
Например, спулер принтера вряд ли будет вызываться из более чем одного места (меню «Печать»), поэтому вы можете использовать мьютексы для решения проблемы одновременного доступа.
Простой регистратор является наиболее очевидным примером возможного допустимого Singleton, но это может измениться с более сложными схемами регистрации.
Чтение файлов конфигурации, которые следует читать только во время запуска, и их инкапсуляция в Singleton.
Properties.Settings.Default
.NET.
Вы используете singleton, когда вам нужно управлять общим ресурсом. Например, спулер принтера. Ваше приложение должно иметь только один экземпляр спулера, чтобы избежать конфликтующих запросов на один и тот же ресурс.
Или соединение с базой данных или файловый менеджер и т. Д.
Синглеты только для чтения, хранящие некоторое глобальное состояние (язык пользователя, путь к файлу справки, путь к приложению), являются разумными. Будьте осторожны с использованием синглетонов для управления бизнес-логикой - один почти всегда заканчивается множеством
Управление подключением (или пулом подключений) к базе данных.
Я бы также использовал его для извлечения и хранения информации о внешних файлах конфигурации.
Один из способов использования синглтона - это охватить случай, когда должен быть один «посредник», контролирующий доступ к ресурсу. Синглтоны хороши в регистраторах, потому что они обеспечивают доступ, скажем, к файлу, который может быть записан только в эксклюзивный файл. Для чего-то вроде ведения журнала они предоставляют способ абстрагирования записи в нечто вроде файла журнала - вы можете поместить механизм кэширования в свой синглтон и т. Д.
Также подумайте о ситуации, когда у вас есть приложение с множеством окон / потоков / и т. Д., Но для которого требуется одна точка связи. Однажды я использовал один для управления заданиями, которые я хотел, чтобы мое приложение запускалось. Синглтон отвечал за сериализацию работ и отображение их статуса в любой другой части программы, которая была заинтересована. В этом сценарии вы можете рассматривать одиночный объект как своего рода «серверный» класс, работающий внутри вашего приложения ... HTH
При управлении доступом к ресурсу, который используется всем приложением, следует использовать одиночный код, и было бы разрушительным иметь несколько экземпляров одного и того же класса. Обеспечение того, чтобы доступ к общим ресурсам был безопасным для потоков, является одним из очень хороших примеров того, где этот тип шаблона может быть жизненно важным.
При использовании Singletons вы должны убедиться, что вы случайно не скрываете зависимости. В идеале синглеты (как и большинство статических переменных в приложении) должны быть установлены во время выполнения кода инициализации приложения (static void Main () для исполняемых файлов C #, static void main () для исполняемых файлов java) и затем переданы в все другие классы, которые создаются, которые требуют этого. Это помогает вам поддерживать тестируемость.
Практический пример синглтона можно найти в Test :: Builder , классе, который поддерживает практически все современные модули тестирования Perl. Синглтон Test :: Builder хранит и обрабатывает состояние и историю процесса тестирования (исторические результаты теста, подсчитывает количество выполненных тестов), а также такие вещи, как, например, результаты теста. Все это необходимо для координации нескольких модулей тестирования, написанных разными авторами, для совместной работы в одном сценарии тестирования.
История Test :: Builder's singleton является образовательной. Вызов new()
всегда дает вам один и тот же объект. Во-первых, все данные были сохранены как переменные класса, и в самом объекте ничего не было. Это работало, пока я не хотел протестировать Test :: Builder сам с собой. Затем мне понадобилось два объекта Test :: Builder, один из которых был настроен как фиктивный, для захвата и тестирования его поведения и вывода, а другой - для реального тестового объекта. В этот момент Test :: Builder был преобразован в реальный объект. Объект-одиночка хранился как данные класса и new()
всегда возвращал его. create()
был добавлен, чтобы сделать свежий объект и включить тестирование.
В настоящее время пользователи хотят изменить некоторые поведения Test :: Builder в своем собственном модуле, но оставить другие в покое, в то время как история тестов остается общей для всех модулей тестирования. Сейчас происходит то, что монолитный объект Test :: Builder разбивается на более мелкие части (история, выходные данные, формат ...), а экземпляр Test :: Builder собирает их вместе. Теперь Test :: Builder больше не должен быть одиночным. Его составляющие, как и история, могут быть. Это толкает негибкую необходимость синглтона на уровень. Это дает больше гибкости пользователю, чтобы смешивать и сочетать части. Меньшие одноэлементные объекты теперь могут просто хранить данные, а их содержащие объекты решают, как их использовать. Он даже позволяет классам, не являющимся Test :: Builder, подыгрывать, используя историю Test :: Builder и выходные синглтоны.
Похоже, что существует толчок между координацией данных и гибкостью поведения, которую можно смягчить, поместив единичный элемент вокруг просто общих данных с наименьшим количеством поведения, насколько это возможно, для обеспечения целостности данных.
Когда вы загружаете объект свойств конфигурации, либо из базы данных, либо из файла, полезно иметь его как одиночный; нет причин продолжать перечитывать статические данные, которые не изменятся во время работы сервера.
Вы можете использовать Singleton при реализации шаблона State (как показано в книге GoF). Это связано с тем, что конкретные классы State не имеют собственного состояния и выполняют свои действия в контексте контекстного класса.
Вы также можете сделать Abstract Factory синглтоном.
setState()
ответственности за принятие решения о создании государства. Это помогает, если ваш язык программирования поддерживает шаблоны или шаблоны. Вместо Singleton вы можете использовать шаблон Monostate , в котором создание экземпляра объекта состояния заканчивается повторным использованием одного и того же объекта глобального / статического состояния. Синтаксис для изменения состояния может остаться неизменным, так как ваши пользователи не должны знать, что созданное состояние является Monostate.
Общие ресурсы. Особенно в PHP, класс базы данных, класс шаблона и класс депо глобальной переменной. Все должны быть общими для всех модулей / классов, которые используются в коде.
Это истинное использование объекта -> класс шаблона содержит шаблон страницы, который создается, и он формируется, добавляется, изменяется модулями, которые добавляются к выводу страницы. Это должно быть сохранено как один экземпляр, чтобы это могло произойти, и то же самое относится к базам данных. С помощью единой базы данных с общей базой данных все классы модулей могут получать доступ к запросам и получать их без необходимости их повторного запуска.
Синглтон депо глобальных переменных предоставляет вам глобальное, надежное и легко используемое хранилище переменных. Это приводит в порядок ваш код. Представьте, что все значения конфигурации в массиве представлены в виде синглтона, например:
$gb->config['hostname']
или все языковые значения в массиве, такие как:
$gb->lang['ENTER_USER']
В конце выполнения кода для страницы вы получите, скажем, зрелый код:
$template
Синглтон, $gb
синглтон, который имеет массив lang для замены в нем, и весь вывод загружен и готов. Вы просто заменяете их ключами, которые теперь присутствуют в значении страницы зрелого объекта шаблона, а затем передаете их пользователю.
Большим преимуществом этого является то, что вы можете выполнять ЛЮБУЮ постобработку, что вам нравится. Вы можете передать все языковые значения в google translate или другую службу перевода и получить их обратно, а также заменить их на свои места, например, переведенные. или, вы можете заменить в структуре страницы, или, строки содержимого, как вы хотите.
Может быть очень прагматично настраивать специфические проблемы инфраструктуры как одиночные или глобальные переменные. Мой любимый пример этого - платформы внедрения зависимостей, которые используют синглтоны, чтобы действовать в качестве точки подключения к среде.
В этом случае вы берете зависимость от инфраструктуры, чтобы упростить использование библиотеки и избежать ненужной сложности.
Я использую его для объекта, инкапсулирующего параметры командной строки при работе с подключаемыми модулями. Основная программа не знает, какие параметры командной строки используются для загружаемых модулей (и даже не всегда знает, какие модули загружаются). например, основные нагрузки A, которые сами по себе не нуждаются ни в каких параметрах (поэтому, почему он должен брать дополнительный указатель / ссылку / что-либо, я не уверен - выглядит как загрязнение), затем загружают модули X, Y и Z. Два из них, скажем, X и Z, нужны (или принимают) параметры, поэтому они перезванивают в синглтон командной строки, чтобы сообщить ему, какие параметры принимать, и во время выполнения они перезванивают, чтобы узнать, действительно ли пользователь указал какой-либо из них.
Во многих отношениях синглтон для обработки параметров CGI будет работать аналогично, если вы используете только один процесс на запрос (другие методы mod_ * не делают этого, поэтому было бы плохо - таким образом, аргумент, который говорит, что вы не должны ' t использовать синглтоны в мире mod_cgi на случай, если вы портируете на mod_perl или любой другой мир).
Пример с кодом, возможно.
Здесь ConcreteRegistry - это одиночная игра в покер, которая позволяет поведению по всему дереву пакетов получать доступ к нескольким основным интерфейсам игры (т. Е. К фасадам модели, вида, контроллера, среды и т. Д.):
http://www.edmundkirwan.com/servlet/fractal/cs1/frac-cs40.html
Издание
1 - комментарий к первому ответу:
Я не согласен со статическим классом Logger. это может быть практичным для реализации, но не может быть заменено для модульного тестирования. Статический класс не может быть заменен тестовым двойником. Если вы не выполните модульное тестирование, вы не увидите здесь проблемы.
2 - Я стараюсь не создавать синглтон вручную. Я просто создаю простой объект с конструкторами, которые позволяют мне вводить коллабораторов в объект. Если бы мне нужен был синглтон, я бы использовал инфраструктуру инъекций зависимостей (Spring.NET, Unity для .NET, Spring для Java) или некоторые другие.
ILogger logger = Logger.SingleInstance();
если этот метод является статическим и возвращает статически сохраненный экземпляр ILogger. Вы использовали пример «структуры внедрения зависимостей». Почти все DI-контейнеры являются синглетонами; их конфигурации определены статически и в конечном итоге доступны из / сохранены в едином интерфейсе поставщика услуг.