Альтернативы синглетонам / глобалам


16

Я слышал бесчисленное количество раз о ловушках синглетонов / глобалов, и я понимаю, почему их так часто осуждают.

Чего я не понимаю, так это элегантной, не грязной альтернативы. Кажется, что альтернатива использованию Singletons / globals всегда включает пропускание объектов на миллион уровней вниз через объекты вашего движка, пока они не достигнут объектов, которые в них нуждаются.

Например, в моей игре я предварительно загружаю некоторые ресурсы при запуске игры. Эти активы не используются намного позже, когда игрок перемещается по главному меню и входит в игру. Должен ли я передавать эти данные из моего объекта Game, в мой объект ScreenManager (несмотря на тот факт, что только один экран действительно заботится об этих данных), затем в соответствующий объект Screen и где-либо еще?

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

Это тот случай, когда синглтон будет хорошей вещью, или есть какое-то элегантное решение, которое мне не хватает?

Ответы:


16

Не объединяйте одиночные и глобальные числа. Хотя обычно необходимы какие-то глобальные переменные, синглтон - это не просто замена глобальной переменной, но, прежде всего, способ обойти проблемы статического порядка инициализации в C ++ ( и FQA ). (В других языках это способ обойти различные языковые недостатки, такие как отсутствие глобальных переменных и голых функций.)

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

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


+1. Хороший ответ и спасибо за ссылку шаблона службы поиска. Это очень интересное чтение.
bummzack

1

Если вы не можете / не можете иметь одну часть кода волшебным образом «знать» о некоторых данных, то это нужно будет как-то передать. Однако это не означает, что оно обязательно должно быть передано только через аргументы.

В вашем примере, не могли бы вы иметь какой-то «AssetManager», который бы загружал и сохранял активы, и тогда ScreenManager нужно было бы только дать ссылку на это (возможно, при создании)? В этом смысле вы передаете ссылки на ресурсы, заключенные в другой объект, и вы можете передать их один раз при инициализации, а не передавать их функции листьев, когда это требуется.

ИМХО, что AssetManager, будучи тем видом, который вам нужен только один, может быть синглтоном. При условии, что вы понимаете подводные камни и специально программируете их, чтобы избежать их (предположим, что синглтон будет доступен одновременно из нескольких потоков, и каждый раз вы будете ударять себя вилкой, когда вы делаете что-то, что нужно заблокировать), а затем выбивайте себя из строя.


1

Я думаю, что Джейсон Д абсолютно прав - вот как я бы с этим справился:

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

В игре:

assetManager = new AssetManager();
screenManager = new ScreenManager();
screenManager.assetManager = assetManager;

В ScreenManager:

screen = new Screen();
screen.assetManager = assetManager;

На экране:

myAsset = assetManager.getBitmp("lava.png");

Теперь все экраны имеют доступ к любым необходимым ресурсам. Это не сложнее и не безумнее, чем использование глобалов или синглетонов, и у вас есть возможность иметь 2 экземпляра Game, запущенных в одном приложении без столкновений. Когда-то мне нужно было сделать игру, состоящую из 8 мини-игр, которые использовали одни и те же базовые классы / рамки. Мне пришлось провести рефакторинг всех моих глобалов / синглетонов, чтобы использовать этот стиль передачи ссылок, и я никогда не оглядывался назад. Единственными вещами, которые должны быть глобальными, являются вещи, которые могут физически существовать только один раз, такие как аудио, сеть, ввод / вывод и т. Д.


0

Вы можете использовать шаблон Factory для замены Singleton . Затем класс фабрики контролирует, сколько экземпляров вы можете создать, что вы можете легко изменить позже, когда обнаружите, что вам нужно более одного AssetManager. Как указано в этой статье :

Это дает вам всю гибкость Синглтона, с таким количеством проблем.


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

статические методы так же гибки, как гранит. Каждый раз, когда вы используете его, вы отливаете часть своей программы в бетон. Просто убедитесь, что у вас не зажата нога, потому что вы смотрите, как она затвердеет. Когда-нибудь вы будете удивлены, что, черт возьми, вам действительно нужна другая реализация этого чертового класса PrintSpooler, и это должны быть интерфейс, фабрика и набор классов реализации. D'о!

Речь идет о статических методах, но также может применяться к статическим классам.


Что вы подразумеваете под "сделать класс статичным"? C ++ не имеет статических классов. Вы имеете в виду только статические методы? Зачем тогда иметь класс вместо пространства имен?

1
@Joe: Хорошо, вопрос не сосредоточен на C ++, поскольку я понял это. В C # или Java вы можете создавать статические классы, и я имею в виду их. Кроме того, как я уже сказал, статические классы в большинстве случаев не являются оптимальным решением, но в редких случаях они могут работать как глобальные.
Майкл Клемент
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.