如何在多个项目中实现 Cocoa Touch 中的服务定位器模式?

发布于 2024-09-12 18:14:26 字数 1428 浏览 9 评论 0原文

这是一个困扰我一段时间的问题。我对其中一些模式还很陌生,所以如果我错误地使用了任何术语,您必须原谅我(并纠正我)。

我的方法

我创建了一个游戏引擎。我的游戏引擎中的所有对象都使用控制反转来获取依赖项。这些依赖项都实现协议,除了在引导阶段之外,从不直接在项目中访问。为了获取这些对象,我有了服​​务定位器的概念。服务定位器的工作是定位一个符合特定协议的对象并返回它。它很像工厂,但它也应该处理依赖关系。

为了向服务定位器提供服务,我有所谓的服务说明符。服务定位器了解项目中的所有服务说明符,并且当请求对象时,尝试从每个服务说明符获取符合所提供协议的对象实例。然后该对象被返回给调用者。这种设置的优点是服务说明符也知道服务定位器,因此如果它有任何依赖项,它只向服务定位器询问这些特定的依赖项。

举个例子,我有一个名为 HighScoreManager 的对象。 HighScoreManager 实现 PHighScoreManager 协议。任何时候,如果需要 PHighScoreManager 的实例,可以通过调用来检索它:

id<PHighScoreManager> highScoreManager = [ServiceLocator resolve: @protocol(PHighScoreManager)];

因此,控制反转。然而,大多数时候甚至没有必要这样做,因为大多数类都位于服务说明符中,如果需要 PHighScoreManager 作为依赖项,则可以通过服务定位器检索它。因此,我有一个很好的扁平化控制反转方法。

我的问题

因为我希望共享游戏引擎中的代码,所以我将其编译为静态库。这对于其他一切来说都很棒,但对于服务定位器来说似乎有点棘手。问题是有些服务会根据游戏的不同而变化。在上面的例子中,一场比赛中的得分可能是时间,而在另一场比赛中可能是分数。因此,HighScoreManager 依赖于 PHighScoreCreator 的实例,后者告诉它如何创建 PScore 对象。

为了向 HighScoreManager 提供 PHighScoreCreator,我需要为我的游戏提供一个服务说明符。我能想到的实现这一点的唯一方法是使用 Cocoa 版本的反射。经过深入研究,我发现可以通过 NSBundle 发现类,但似乎无法获取当前包。因此,如果我希望能够搜索到我的服务说明符,我必须将我的游戏逻辑编译到它自己的包中,然后让引擎搜索这个包并加载它。为了做到这一点,我必须创建第三个项目来容纳引擎代码和游戏逻辑包,而实际上我只想拥有一个使用引擎静态库的游戏项目。

我真正的问题

因此,在所有这些之后,我的问题是

  1. 是否有更好的方法来完成我想要在 Cocoa Touch 中完成的任务,或者
  2. 是否有一种方法可以发现符合我的服务的类来自主包的说明符协议?

感谢您的帮助并花时间阅读问题。

-螺旋状

This is a problem which has been bugging me for a while now. I'm still pretty new with some of these patterns so you'll have to forgive me (and correct me) if I use any of the terms incorrectly.

My Methodology

I've created a game engine. All of the objects in my game engine use inversion of control to get dependencies. These dependencies all implement protocols and are never accessed directly in the project, other than during the bootstrapping phase. In order to get these objects, I have the concept of a service locator. The service locator's job is to locate an object which conforms to a specific protocol and return it. It's a lot like a factory, but it should handle the dependencies as well.

In order to provide the services to the service locator, I have what I call service specifiers. The service locator knows about all of the service specifiers in the project, and when an object is requested, attempts to get an instance of an object conforming to the provided protocol from each of them. This object is then returned to the caller. What's cool about this set up is the service specifier also knows about a service locator, so if it has any dependencies, it just asks the service locator for those specific dependencies.

To give an example, I have an object called HighScoreManager. HighScoreManager implements the PHighScoreManager protocol. At any time if an instance of PHighScoreManager is required, it can be retrieved by calling:

id<PHighScoreManager> highScoreManager = [ServiceLocator resolve: @protocol(PHighScoreManager)];

Thus, inversion of control. However, most of the time it isn't even necessary to do this, because most classes are located in a service specifier, if one required PHighScoreManager as a dependency, then it is retrieved through the service locator. Thus, I have a nice flat approach to inversion of control.

My Problem

Because I want the code from my game engine to be shared, I have it compiled as a static library. This works awesome for everything else, but seems to get a little tricky with the service locator. The problem is some services change on a game to game basis. In my above example, a score in one game might be a time and in another it might be points. Thus, HighScoreManager depends on an instance of PHighScoreCreator, which tells it how to create a PScore objecct.

In order to provide PHighScoreCreator to HighScoreManager, I need to have a service specifier for my game. The only way I could think of to accomplish this was to use the Cocoa version of reflections. After digging around, I found out classes were discoverable through NSBundle, but it seems there's no way to get the current bundle. Thus, if I want to be able to search out my service specifiers, I would have to compile my game logic into its own bundle, and then have the engine search out this bundle and load it. In order to do this I'd have to create a third project to house both the engine code and the game logic bundle, when in reality I'd like to just have a game project which used the engine static library.

My Real Question

So after all of that, my question is

  1. Is there a better way to do what I'm trying to accomplish in Cocoa Touch, or
  2. Is there a way to discover classes which conform to my service specifier protocol from the main bundle?

Thanks for the help and taking the time to read the question.

-helixed

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

追我者格杀勿论 2024-09-19 18:14:26

看一下:

  • +[NSBundle mainBundle];
  • +[NSBundle bundleForClass:];
  • +[NSBundle bundleWithIdentifier:];
  • +[NSBundle allBundles];
  • +[NSBundle allFrameworks];

这些允许您在运行时以编程方式与各种捆绑包进行交互。一旦你有了一个可以使用的包,你就可以采用多种策略来找到你正在寻找的特定类。例如:

  1. 检索包标识符 - 这将是一个 NSString,如@“com.example.GameEngineClient”。
  2. 通过删除最后一个点之前的所有内容,或者用下划线或其他方式替换所有点,然后附加预定义的协议名称,将其转换为合法的 Objective-C 类名。例如,上面的协议可能会产生类似@“GameEngineClient_PHighScoreManager”的字符串。
  3. 使用 NSClassFromString() 获取您的协议的包的指定类。

现在您可以创建包作者提供的类的实例,该实例实现您指定的任何协议。

Objective-C 运行时是一件美妙的事情!

Have a look at:

  • +[NSBundle mainBundle];
  • +[NSBundle bundleForClass:];
  • +[NSBundle bundleWithIdentifier:];
  • +[NSBundle allBundles];
  • +[NSBundle allFrameworks];

These allow you to interact programmatically with the various bundles at runtime. Once you have a bundle to work with there are a number of strategies you could employ to find the specific class(es) you are looking for. For example:

  1. Retrieve the bundle identifier — this will be an NSString like @"com.example.GameEngineClient".
  2. Transform it into a legal Objective-C class name by stripping everything before the last dot, or replacing all the dots with underscores, or whatever, and then appending a predefined protocol name. Your protocol from above, for instance, might result in a string like @"GameEngineClient_PHighScoreManager".
  3. Get the bundle's designated class for your protocol using NSClassFromString().

Now you can create an instance of the class provided by the bundle author, that implements whatever protocol you have specified.

The Objective-C runtime is a beautiful thing!

奈何桥上唱咆哮 2024-09-19 18:14:26

听起来您需要使用 的功能Objective-C 运行时。首先,您可以通过 objc_getClassList 获取所有可用类的列表。然后,您可以迭代所有类并使用 class_conformsToProtocol 检查它们是否符合您的协议。您不应在此处使用 +conformsToProtocol: 消息,因为运行时中存在不支持此选择器的类。

Sounds like you need to use the functions of the Objective-C runtime. First you can get a list of all available classes via objc_getClassList. Then you can iterate over all the classes and check if they conform to your protocol with class_conformsToProtocol. You shouldn’t use +conformsToProtocol: messages here, since there are classes in the runtime that don’t support this selector.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文