C++ 正确的单例用例
C++ proper singleton use case
我正在制作一个游戏来练习好design/arch,我想我已经将自己编程为一个支票。我不确定我的架构现在是否干净,一些建议很重要。
我有一个 class Game
包含 class GraphicsManager
class InputManager
和 class StateManager
的实例
图形管理器像输入管理器一样处理 sdl 内容。状态管理器是一堆抽象类型 class GameState
每一帧,游戏都会在状态堆栈顶部的 GameState 上调用更新,并向所述状态传递一个 StateManager 指针,以便 GameState 可以推送新状态或从堆栈中弹出自身。
GameState* currentState = stateManager.getState() //return top
currentState.update(&stateManager) // state might change here
问题是当我介绍我的 class Screen
时。 Screen 应该由 GameState 的各种实现拥有,所以如果我有 OpeningState,我希望它拥有 OpeningScreen。
问题是,Screen 需要访问 Graphics 实例以获取指向 SDL_Renderer 的指针,但我看不到向下传递该指针的好地方。从 Game --> GameState --> Screen 传递指针感觉有点古怪。
我觉得我需要将每个 'Manager' 变成一个单例,但如果有更优雅的东西我很想学习
这是我的参考代码 https://github.com/MicahMartin/FightingGame/tree/master/src
您可以使用单例而不是显式注入依赖项,但请注意,这种设计会导致代码的可测试性降低和组件之间的功能耦合度高。其他生态系统已经解决了这个问题,引入了以声明方式注入依赖项的方法,这要归功于提供用 "test doubles" 替换实际依赖项的机制的框架。查看 Spring MVC 如何与 Java 应用程序一起工作。
如果您确定这是您所需要的,您可以使用类似(但仍然不太灵活)的方法来实现 "service locator" 模式,该模式包含一个点,该点包含指向 "injectable" 个对象
ServiceLocator::inject<Screen*>(new Screen () , "screen1")
//
...
//
Screen* s = ServiceLocator::getInstance<Screen*>("screen1")
这样可以避免传递指针,同时可以更加解耦。现在假设您有一个继承自 Screen 的 MockScreen。还假设您要测试方法 int MyClass::workWithScreen(),它依赖于用标签 "screen1" 引用的 Screen 实例。您可以像
这样编写测试
Myclass unitUnderTest;
ServiceLocator::inject<Screen*>(new MockScreen () , "screen1");
int result = unitUnderTest.workWithScreen()
assert ....
我将留给您作为实施服务定位器的练习!
请记住,通过这种方式,您也在代码中引入了依赖注入的缺点:其中最糟糕的是您失去了对组件布局设计的控制,每个对象都可以访问每个对象目的。当您开发一些基于可靠且众所周知的架构模式(控制器 -> 服务 -> 存储库 -> 数据库 然后返回)的 REST api 处理程序时,这不是一个大问题,但你必须小心在其他情况下!
我正在制作一个游戏来练习好design/arch,我想我已经将自己编程为一个支票。我不确定我的架构现在是否干净,一些建议很重要。
我有一个 class Game
包含 class GraphicsManager
class InputManager
和 class StateManager
图形管理器像输入管理器一样处理 sdl 内容。状态管理器是一堆抽象类型 class GameState
每一帧,游戏都会在状态堆栈顶部的 GameState 上调用更新,并向所述状态传递一个 StateManager 指针,以便 GameState 可以推送新状态或从堆栈中弹出自身。
GameState* currentState = stateManager.getState() //return top
currentState.update(&stateManager) // state might change here
问题是当我介绍我的 class Screen
时。 Screen 应该由 GameState 的各种实现拥有,所以如果我有 OpeningState,我希望它拥有 OpeningScreen。
问题是,Screen 需要访问 Graphics 实例以获取指向 SDL_Renderer 的指针,但我看不到向下传递该指针的好地方。从 Game --> GameState --> Screen 传递指针感觉有点古怪。
我觉得我需要将每个 'Manager' 变成一个单例,但如果有更优雅的东西我很想学习
这是我的参考代码 https://github.com/MicahMartin/FightingGame/tree/master/src
您可以使用单例而不是显式注入依赖项,但请注意,这种设计会导致代码的可测试性降低和组件之间的功能耦合度高。其他生态系统已经解决了这个问题,引入了以声明方式注入依赖项的方法,这要归功于提供用 "test doubles" 替换实际依赖项的机制的框架。查看 Spring MVC 如何与 Java 应用程序一起工作。
如果您确定这是您所需要的,您可以使用类似(但仍然不太灵活)的方法来实现 "service locator" 模式,该模式包含一个点,该点包含指向 "injectable" 个对象
ServiceLocator::inject<Screen*>(new Screen () , "screen1")
//
...
//
Screen* s = ServiceLocator::getInstance<Screen*>("screen1")
这样可以避免传递指针,同时可以更加解耦。现在假设您有一个继承自 Screen 的 MockScreen。还假设您要测试方法 int MyClass::workWithScreen(),它依赖于用标签 "screen1" 引用的 Screen 实例。您可以像
这样编写测试Myclass unitUnderTest;
ServiceLocator::inject<Screen*>(new MockScreen () , "screen1");
int result = unitUnderTest.workWithScreen()
assert ....
我将留给您作为实施服务定位器的练习!
请记住,通过这种方式,您也在代码中引入了依赖注入的缺点:其中最糟糕的是您失去了对组件布局设计的控制,每个对象都可以访问每个对象目的。当您开发一些基于可靠且众所周知的架构模式(控制器 -> 服务 -> 存储库 -> 数据库 然后返回)的 REST api 处理程序时,这不是一个大问题,但你必须小心在其他情况下!