模式:单例 vs. 静态变量和方法方法 [英] Patterns: Singletons vs. Static vars and methods approach

查看:23
本文介绍了模式:单例 vs. 静态变量和方法方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我读了很多关于单例模式的书.我目前正在使用它在我的第一个应用程序中存储全局状态组.我已经到了想知道实现 API 客户端类和类似的方法的地步.

I am reading a lot about the Singleton Pattern. I am currently using it to store groups of global state in my first app. I am reaching a point where I wonder which approach to implement API client classes and similar with.

具有静态变量静态函数的结构是否存在相同的问题?

Are Structs with static vars and static functions having the same issues?

为了说明我的意思,我尝试编写两次相同的高度简化且完全相同(?)的场景.

To illustrate what I mean, I've tried to write the same heavily simplified and exact same(?) scenario twice.

1.视图控制器使用的单例:

struct APIClientSingletonClass {

    static let shared = APIClientSingletonClass()

    var stateOfSomehting: Bool = true
    var stateOfSomehtingMore: Bool = false
    var stateNumber: CGFloat = 1234
    var stateOfSomehtingComputed: CGFloat {
        return stateNumber * 10
    }

    func convertSomethingToSomethingElse() {
        // calling method in self like this:
        otherMethod()
    }
    func otherMethod() {
        // doing stuff here
    }
}



// Calling APIClient from outside:
class ViewControllerTalkingToSingleton: UIViewController {

    var api = APIClientSingletonClass.shared

    override func viewDidLoad() {
        super.viewDidLoad()
        api.convertSomethingToSomethingElse()
        api.stateOfSomehting = false
    }
}

2.另一种方法:

struct APIClientStruct {

    static var stateOfSomehting: Bool = true
    static var stateOfSomehtingMore: Bool = false
    static var stateNumber: CGFloat = 1234
    static var stateOfSomehtingComputed: CGFloat {
        return stateNumber * 10
    }

    static func convertSomethingToSomethingElse() {
        // calling method in self like this:
        APIClientStruct.otherMethod()
    }

    static func otherMethod() {
        // doing stuff here
    }
}


// Calling APIClient from outside:
class ViewControllerTalkingToStruct: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        APIClientStruct.convertSomethingToSomethingElse()
        APIClientStruct.stateOfSomehting = false
    }
}

大家怎么看?方法 2 是否陷入了使单身人士成为双刃剑的陷阱?

What do you guys think? Is approach 2 falling into the same traps that seem to make Singletons such a double-edged sword?

任何输入都非常感谢!柏林最佳

Any input is really appreciated! Best from Berlin

这个帖子很有趣,但我不确定它是否真的与我的问题有关:

This thread is pretty interesting, but I'm not sure it really relates to my question:

静态类和单例模式的区别?

由于这个话题有很多观点,我来具体说明一下:我的方法 2 是否对测试和代码可维护性有同样的问题影响?

Since there are many perspectives on this topic, let me specify: Does my approach 2 have the same problem implications with testing and code maintainability?

推荐答案

基于类的单例是可行的方法,前提是您为测试提供依赖注入.这样做的方法是为您的应用程序创建一个单例,称为 DependencyManager.在您的 AppDelegate(或从其他类,如果需要)中,您可以创建任何想要挂在 DependencyManager 上的控制器、网络服务、领域模型等,然后将它们分配给 DependencyManager.您的单元测试将跳过此代码.

A class-based singleton is the way to go, provided you accommodate for dependency injection for your tests. The way to do this is to create a single singleton for your app, called, say, DependencyManager. In your AppDelegate (or from other classes if needed), you'd create whatever controllers, network services, realm models, etc you want to hang on your DependencyManager, and then assign them to the DependencyManager. This code would be skipped by your unit tests.

然后,您的单元测试可以访问 DependencyManager(从而在第一次访问期间实例化 DependencyManager),并在每个单元测试所需的任何程度上使用这些控制器和服务的模拟版本填充它.

Your unit tests can then access the DependencyManager (and thus instantiate the DependencyManager during first access), and populate it with mock versions of those controllers and services to whatever degree each unit test desires.

您的 UIViewControllers、您的 MVVM 视图模型等...可以作为单例访问 DependencyManager,从而获得真实的控制器和服务,或它们的模拟版本,具体取决于您是运行应用程序还是单元测试.

Your UIViewControllers, your MVVM view models, etc... can access the DependencyManager as a singleton, and thus get either the real controllers and services, or a mock version of them, depending on if you're running the app or unit tests.

如果你正在做 MVVM,我还建议当 UIViewController 将要创建它的视图模型类时,它首先检查 DependencyManager 中的一个特殊属性,以查看是否存在模拟视图模型.单个属性可以用于此目的,因为一次只会测试一个 UIViewController.它会使用该属性而不是为自己创建一个新的视图模型.通过这种方式,您可以在测试每个 UIViewController 时模拟您的视图模型.(还有其他技巧可以支持单个 UIViewController 进行测试,但我不会在这里介绍).

If you're doing MVVM, I also recommend that when a UIViewController is about to create its view model class, that it first checks a special property in the DependencyManager to see if a mockViewModel exists. A single property can serve this purpose, as only one of your UIViewControllers ever would be tested at once. It'd use that property instead of creating a new view model for itself. In this way, you can mock your view models when testing each UIViewController. (There's other tricks involved to being able to prop up a single UIViewController for testing, but I won't cover that here).

请注意,以上所有内容都可以很好地与还想使用故事板和/或笔尖的应用配合使用.人们对故事板如此失望,因为他们无法弄清楚如何为他们的视图控制器进行模拟服务的依赖注入.嗯,以上就是解决方案!只需确保在您的 AppDelegate 中在设置 DependencyManager 后加载故事板.(从 info.plist 中删除故事板名称,并在 AppDelegate 中自行实例化).

Note that all of the above can work very nicely with an app that also wants to use storyboards and/or nibs. People are so down on storyboards because they can't figure out how to do dependency injection of mock services for their view controllers. Well, the above is the solution! Just make sure in your AppDelegate to load the storyboard AFTER setting up the DependencyManager. (Remove the storyboard name from your info.plist, and instantiate it yourself in AppDelegate).

我以这种方式编写了一些已发布的应用程序,以及一些用于 SDK 的示例应用程序以及测试.我强烈推荐这种方法!并且一定要在每个这样的类的开发过程中或至少在开发之后立即编写单元测试和视图控制器测试,否则你将永远无法使用它们!

I've written a few shipped apps this way, as well as some sample apps for an SDK, along with the tests. I highly recommend the approach! And be sure to write your unit tests and viewController tests either during or at least immediately after development of each such class, or you'll never get around to them!

这篇关于模式:单例 vs. 静态变量和方法方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆