Я ненавижу бить мертвую лошадь, и все же за последние несколько дней я прочитал так много противоречивых статей, касающихся использования шаблона синглтона.
Этот вопрос не может бытьо том, что является лучшим выбором в целом, скорее, что имеет смысл для моего варианта использования.
Проект питомца, над которым я работаю, - это игра.Часть кода, над которым я сейчас работаю, я склоняюсь к использованию одноэлементного шаблона.
Варианты использования следующие:
- глобально доступный регистратор.
- менеджер рендеринга OpenGL.
- доступ к файловой системе.
- доступ к сети.
- и т. Д.
Теперь для пояснения,более чем пара из вышеупомянутых требует общего состояния между доступами.Например, регистратор оборачивает библиотеку журналов и требует указателя на выходной журнал, сети требуется установленное открытое соединение и т. Д.
Теперь из того, что я могу сказать, более рекомендуется избегать синглетонов, поэтомудавайте посмотрим, как мы можем это сделать.Во многих статьях просто говорится, чтобы создать экземпляр вверху и передать его в качестве параметра в любое место, где это необходимо.Хотя я согласен с тем, что это технически выполнимо, возникает вопрос: как управлять потенциально огромным количеством параметров?Хорошо, что приходит на ум, оборачивая различные экземпляры в своего рода «контекстный» объект и передавая его, затем делая что-то вроде context->log("Hello World")
.Теперь убедитесь, что это не так уж и плохо, но что, если у вас есть своего рода фреймворк, подобный так:
game_loop(ctx)
->update_entities(ctx)
->on_preupdate(ctx)
->run_something(ctx)
->only use ctx->log() in some freak edge case in this function.
->on_update(ctx)
->whatever(ctx)
->ctx->networksend(stuff)
->update_physics(ctx)
->ctx->networksend(stuff)
//maybe ctx never uses log here.
Вы поймете точку ... в некоторых областях некоторые аспекты "ctx" aren 'Он никогда не использовался, но вы все равно застряли, передавая его буквально повсюду, на случай, если вы захотите отладить что-либо в прямом эфире с помощью регистратора, или, возможно, на более поздних этапах разработки вы действительно захотите создать сеть или что-то еще в этом разделе кода.
Я чувствую, что приведенный выше пример больше подходит для глобально доступного синглтона, но я должен признать, что я исходил из C # / Java / JS-фона, который может окрасить мой взгляд.Я хочу перенять образ мыслей / лучшие практики программиста на C ++, но, как я уже сказал, я не могу найти прямой ответ.Я также заметил, что в статьях, в которых предлагается просто передать «синглтон» в качестве параметра, приводятся лишь очень упрощенные варианты использования, при которых любой согласится с тем, что лучшим вариантом будет использование параметра.
В этом примере игры вы, вероятно,Я не хочу получать доступ к журналу везде, даже если вы не планируете использовать его немедленно.Материал файловой системы может быть закончен, но до тех пор, пока вы не создадите проект, очень трудно сказать, когда / где он будет наиболее полезен.
Я тоже:
- Придерживайтесьиспользование синглетонов для этих случаев использования независимо от того, как люди говорят «зло / зло».
- Оберните все в объект контекста и передайте его буквально везде.(кажется довольно грубым ИМО, но если это «более приемлемый / лучший» способ сделать это, пусть будет так).
- Что-то совершенно другое.(Действительно потерянный относительно того, что это могло бы быть.)
Если вариант 1, с точки зрения производительности, я должен переключиться на использование функций пространства имен и скрытие «частных» переменных / функций в анонимных пространствах имен, таких какбольшинство людей делают в C?(Я предполагаю, что произойдет небольшое повышение производительности, но потом я застряну с необходимостью вызывать метод "init" и "destroy" для некоторых из них, вместо того, чтобы просто позволить конструктору / деструкторусделать это для меня, все еще может стоить?)
Теперь я понимаю, что это может быть немного основано на мнении, но я надеюсь, что все еще могу получить относительно хороший ответ, когда более сложный / вложенный код базынаходится под вопросом.
Редактировать: После долгих размышлений я решил вместо этого использовать шаблон "Service Locator".Чтобы предотвратить глобальный / одноэлементный локатор сервисов, я делаю все, что может использовать сервисы, наследуемые от абстрактного базового класса, который требует передачи сервисного локатора при создании.
Я еще не реализовал все, так что я все еще не уверен, столкнусь ли я с какими-либо проблемами с этим подходом, и все равно хотел бы получить обратную связь, если это разумная альтернатива дилемме синглтон / глобальная область действия.
Я читал, что Service Locator также является своего рода анти-паттерном, в котором говорится, что многие из найденных мною примеров реализовали его с использованием статики и / или как синглтона, возможно, используя его, как я описал, удаляет аспектычто вызывает анти-паттерн?