2015 年,苹果推出了一个新概念——面向协议的编程。我敢肯定,您正在使用这些技巧中的大部分,甚至全部。
案例:您正在使用UserDefaults. 也许是一个简单的保存用户关于黑暗主题偏好的决定。
class PreferenceSaver { var useDarkTheme: Bool { get { UserDefaults.standard.bool(forKey: "useDarkTheme") } set { UserDefaults.standard.set(newValue, forKey: "useDarkTheme") } } }
|
到目前为止,这将在生产中完美运行,但是......如果需求发生变化怎么办?也许标准用户默认设置不够好?对于一个属性,更改没有问题,但是如果有 20 个首选项... 我将重构它以使其更有用:)
class PreferenceSaver { private let defaults: UserDefaults init(defaults: UserDefaults = .standard) { self.defaults = defaults } var useDarkTheme: Bool { get { defaults.bool(forKey: "useDarkTheme") } set { defaults.set(newValue, forKey: "useDarkTheme") } } }
|
没有太大变化,但一切都变了。现在PreferenceSaver有一个新的依赖项,可以在init. 此外,我添加了一个默认值 - 因此代码库中没有任何更改以适应新的初始化。
我们已经完成了一半。现在进行单元测试。要注入UserDefaults模拟,您必须继承并推动整个 Apple 类!这是压倒性的和麻烦的。Defaults 类很简单,那么定位服务、Store Kit 类呢?它们庞大而复杂。
我将再次重构代码。
protocol UserDefaultsProtocol { func bool(forKey: String) -> Bool func set(_ value: Bool, forKey: String) }
extension UserDefaults: UserDefaultsProtocol { }
class PreferenceSaver { private let defaults: UserDefaultsProtocol init(defaults: UserDefaultsProtocol = UserDefaults.standard) { self.defaults = defaults } var useDarkTheme: Bool { get { defaults.bool(forKey: "useDarkTheme") } set { defaults.set(newValue, forKey: "useDarkTheme") } } }
|
再一次,我的更改根本不会影响当前的代码库,因此使用PreferenceSaver. 通过添加协议和扩展UserDefaults,我们拥有原始实现的所有功能,并增加了可测试性!
在测试套件中,将模拟注入PreferenceSaver:
class PreferenceSaverMock: UserDefaultsProtocol { var dictionary = [String: Any]() func bool(forKey: String) -> Bool { return dictionary[forKey] as! Bool } func set(_ value: Bool, forKey: String) { dictionary[forKey] = value } }
|