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



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

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单例属性之外,是否有更优雅的替代方法?

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

为什么不直接将其设置在您需要的地方?

"依赖注入"是一个25美元的术语,用于5美分的概念。 这并不是说这是一个糟糕的术语...
[...]
依赖注入手段 为对象提供其实例变量。真。就是这样。

– 詹姆斯·肖尔:依赖注入揭秘

要么在施工期间这样做

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 客户端单例,并在测试期间执行不需要的网络请求。如果它是从测试类外部设置的,则可以传入一个模拟的网络客户端,该客户端不执行任何请求,但返回测试数据。

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

如果这个问题是关于全局的,你应该看到这个线程:单身人士有什么不好?

但是,如果您想为单例的实现提供更好的设计,您可以尝试这样的事情:

class SingletonExample: NSObject {
    static let sharedInstance: SingletonExample()
}
class OtherSingletonExample: NSObject {
    static let sharedInstance: OtherSingletonExample()
}

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

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

相关内容

  • 没有找到相关文章

最新更新