处理第三方API时正确的系统设计是什么?
Joubert 的这篇博客文章让我大开眼界。我接触过很多 Java 和其他语言的设计模式。但 Objective-C 是一种相当独特的语言。
假设在一个项目中,我们与第三方 API 进行交互,例如 Dropbox 或 Facebook。到目前为止,我一直在做的是将与第三方 API 相关的所有内容组合到一个单例类中。所以我可以从视图控制器中的任何位置访问该类。我可以举个例子: [[DropboxModel sharedInstance] uploadFile:aFile]
然而,正如博客文章指出的,这效率不高,会导致意大利面条代码和糟糕的单元测试。那么设计系统使其模块化且易于使用的最佳方法是什么?
This blog post by Joubert just opened my eyes. I have dealt with a lot of design patterns in Java and other languages. But Objective-C is a rather unique language.
Let's say that in a project we talk with a third party API, like Dropbox or Facebook. What I've been doing so far is to combine everything that has to do with the third party API into a singleton class. So I can access the class from anywhere in my view controllers. I can just go for example: [[DropboxModel sharedInstance] uploadFile:aFile]
However as the blog post noted, this isn't efficient and leads to spaghetti code and bad unit testing. So what is the best way to design the system so that it's modular and easy to use?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我不同意单例会导致意大利面条式代码并且效率低下的观点。然而,单元测试问题是合理的,并且单例确实减少了模块化,因为它们实际上只是花哨的全局变量。
我喜欢 Joubert 的想法,即将单例实例从应用程序委托注入到控制器中(它本身就是一个单例,咳咳)。我认为同样的方法也适合你。
在这些情况下,我可能想在单元测试中使用不同的存根对象,我通常会定义一个协议来表示 API,并使我的“真实”API 对象与我的存根 API 对象一致。我在单元测试中使用存根,在应用程序中使用真实对象。
I would dispute the idea that singletons lead to spaghetti code and are inefficient. However, the unit testing problem is legitimate and singletons do reduce modularity since they are really just fancy global variables.
I like Joubert's idea of injecting the singleton instance into the controller(s) from the app delegate (which is itself a singleton, ahem). I think the same approach would work for you.
What I normally do in these situations where I might want to use a different stub object in unit tests is define a protocol to represent the API and make my "real" API object conform to it and also my stub API object. I use the stub in the unit tests and the real object in the app.
这并不是说这真正解决了与单例相关的任何架构问题,但为了可读性和可打字性,您始终可以在 DropboxModel 头文件中定义一个宏,例如:
Not that this really solves any architectural problems associated with singletons, but for the sake of readability and typability you can always define a macro in your DropboxModel header file, eg:
我通常会创建一个抽象层。这将一个简单的接口包装到您使用的库的调用上,同时让您有机会引入您需要的任何状态(例如变量)。
然后,您可以只公开您需要和使用的内容,并添加您自己的状态、检查,并从一个地方方便地处理库的所有问题。引入“问题”的原因有多种——可能是线程、资源、状态或跨版本的不良行为变化。
大多数库并不意味着只能通过单例使用。在这种情况下,最好(主观)像平常一样创建接口——当然,要注意抽象层背后的约束。从这个意义上说,您只需创建基于对象的接口,这些接口按大小/任务/目的/功能划分——所有这些就像您在编写自己的类时通常所做的那样。
如果您不需要到处都有库,那么我认为包装您需要的内容以最小化依赖关系也很好(在大型项目中越来越重要)。
如果您在各处使用该库,那么您可能也更喜欢使用没有抽象层的调用。
i'll typically create an abstraction layer. this wraps a simple interface onto the library's calls which you use, while giving you a chance to introduce whatever state (e.g. variables) you'll need.
you can then expose only what you need and use, and add your own state, checks, and conveniently deal with all issues of the library from one place. 'issues' may be introduced for several reasons - it could be threading, resources, state, or undesired behavioral changes across versions.
most libraries are not meant to be used solely via a singleton. in such cases, it's best (subjective) to create interfaces as you would normally -- of course, being mindful of the constraints behind the abstraction layer. in that sense, you simply create object based interfaces which are divided by size/task/purpose/functionality -- all as you'd usually do when writing your own classes.
if you don't need the library all over the place, then i think it's also good to wrap what you need to minimize dependencies (increasingly important in large projects).
if you use the library all over the place, then you may also prefer to use the calls without the abstraction layer.