【问题标题】:Alternative to smelly global variables in Swift?Swift 中难闻的全局变量的替代品?
【发布时间】:2016-02-19 16:19:08
【问题描述】:

我已经定义了一个具有静态属性的全局结构,其值在我的许多视图控制器中使用,如下所示:

public struct AppGlobal {
    static var currentUser = UserModel()
    static let someManager = SomeManager()

    // Prevent others from initializing
    private init() { }
}

然后在我的UIViewController 中,我可以这样做:

class MyController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        AppGlobal.currentUser.prop1 = "abc123"
        AppGlobal.someManager.startUpdating()
    }
}

这显然很方便,但闻起来很糟糕。我相信依赖注入在这里会派上用场,但不确定如何。有没有比创建AppGlobal 单例属性更优雅的替代方法?

【问题讨论】:

  • 我通常使用我的管理类的单例实例,它仍然存在“全局方法”的问题,但许多 API 使用相同的技术,我认为最适合这些情况。跨度>

标签: ios swift dependency-injection swift2 global-variables


【解决方案1】:

我不明白为什么需要通过全局状态访问 userModel 或 someManager(是的,单例就是这样)。

为什么不把它放在你需要的地方呢?

“依赖注入”是 5 美分概念的 25 美元术语。 这并不是说这是一个坏词……
[…]
依赖注入手段 给一个对象它的实例变量。真的。就是这样。

– 詹姆斯·肖尔:Dependency Injection Demystified

在构建过程中进行

class C {
    let currentUser: UserModel
    let someManager: SomeManager
    init(currentUser:UserModel, someManger:SomeManager) {
        self.currentUser = currentUser
        self.someManager = someManager
    }
}

或通过属性。如果您需要确保设置了所有属性,请执行以下操作:

class MyController: UIViewController {
    var currentUser: UserModel? {
        didSet{
            self.configureIfPossible()
        }
    }
    var someManager: SomeManager?{
        didSet{
            self.configureIfPossible()
        }
    }

    func configureIfPossible(){
        if let currentUser = self.currentUser, someManager = self.someManager {
            // configure    
        }
    }
}

在我当前的项目中,我们的政策是每个依赖项都必须从类外部可见和可配置。

一个例子:

class LibrarySegmentViewController: BaseContentViewController {
    var userDefaults: NSUserDefaults?
    var previousSorting : LibrarySortingOrder = .AZ
    var sorting : LibrarySortingOrder {
        set{
            self.previousSorting = sorting
            if let filterMode = self.filterMode {
                self.userDefaults?.setInteger(newValue.rawValue, forKey: "\(filterMode)_LibrarySorting")
            }
            self.setupIfReady()
        }
        get{
            if let filterMode = self.filterMode {
                if let s = LibrarySortingOrder(rawValue: self.userDefaults!.integerForKey("\(filterMode)_LibrarySorting")) {
                    return s
                }
            }
            return .Date
        }
    }

}

如您所见,我们甚至使用属性来引用NSUserDefaults.standardUserDefaults()。我们这样做是因为我们可以在测试期间传递新的实例,而不会带来更大的嘲笑麻烦。

这是为什么不直接使用单例的最重要的原因:依赖项是隐藏的,可能会在测试和重构期间咬你。另一个示例是隐藏在代码中并在测试期间执行不需要的网络请求的 API 客户端单例。如果它是从测试类外部设置的,您可以传入一个不执行任何请求但返回测试数据的模拟网络客户端。

因此,即使您使用单例,您也应该将其作为依赖项传递。

【讨论】:

  • 这应该是正确的答案。依赖注入使您的代码更具可测试性,因为它使您的依赖项可替换和可模拟。单例是一种反模式。它们会导致紧密耦合,从而降低代码的可测试性。
【解决方案2】:

如果这个问题是关于全球的,你应该看到这个帖子: What is so bad about singletons?

但是,如果您想为您的单例实现提供更好的设计,您可以尝试以下方法:

class SingletonExample: NSObject {

    static let sharedInstance: SingletonExample()
}

class OtherSingletonExample: NSObject {

    static let sharedInstance: OtherSingletonExample()
}

然后您可以在代码中的任何位置使用SingletonExample.sharedInstanceOtherSingletonExample.sharedInstance

这个想法是将一个单例与另一个隔离开,并将其作为类属性访问,而不是为任何东西创建一个大的全局结构。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-12-09
    • 2017-10-06
    • 1970-01-01
    • 1970-01-01
    • 2021-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多