替代Swift中的臭味全局变量?

Tru*_*an1 2 dependency-injection global-variables ios swift swift2

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

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

    // Prevent others from initializing
    private init() { }
}
Run Code Online (Sandbox Code Playgroud)

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

class MyController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        AppGlobal.currentUser.prop1 = "abc123"
        AppGlobal.someManager.startUpdating()
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Vik*_*ica 5

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

为什么不把它设置在你需要的地方?

对于5美分的概念,"依赖注入"是一个25美元的术语.这并不是说这是一个糟糕的术语......
[...]
依赖注入意味着为对象提供实例变量.真.而已.

- 詹姆斯肖尔:依赖注射神秘化

在建造期间要么这样做

class C {
    let currentUser: UserModel
    let someManager: SomeManager
    init(currentUser:UserModel, someManger:SomeManager) {
        self.currentUser = currentUser
        self.someManager = someManager
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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    
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在我当前的项目中,我们的策略是每个依赖项必须是可见的,并且可以从类外部进行配置.

一个例子:

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
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

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

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

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