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

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

问题描述

我正在阅读很多有关Singleton模式的信息.我目前正在使用它在我的第一个应用程序中存储全局状态组.我到达了一个点,我想知道哪种方法可以实现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是否落入了似乎使Singletons成为双刃剑的陷阱中?

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.

您的UIViewController,您的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中检查一个特殊属性,以查看是否存在mockViewModel.单个属性可以实现此目的,因为一次只能测试您的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).

请注意,以上所有内容都可以与还希望使用情节提要和/或笔尖的应用很好地配合使用.人们之所以对情节提要感到沮丧,是因为他们不知道如何为自己的视图控制器进行模拟服务的依赖注入.好了,以上就是解决方案!只需确保在设置DependencyManager之后在AppDelegate中加载情节提要. (从您的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的示例应用程序以及测试.我强烈推荐这种方法!并且一定要在每个此类开发期间或之后立即编写单元测试和viewController测试,否则您将永远无法解决它们!

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!

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

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