我正在尝试创建一种在 Swift 中构建可比较对象的方法。我觉得我几乎已经有了我所拥有的,但它仍然不是 100% 正确的。
我的目标是拥有一个FlowController
可以创建我们的对象,UIViewControllers
然后为他们提供他们需要的任何依赖项。
我还想做的是使这项工作尽可能松散。
我在这里有一个小例子,但并不理想。我会解释...
这里有两个可以用作组件的对象......Wallet
和User
。
class Wallet {
func topUp(amount: Int) {
print("Top up wallet with £\(amount)")
}
}
class User {
func sayHello() {
Print("Hello, world!")
}
}
然后我们定义一个Component
枚举,其中包含每个案例......
enum Component {
case Wallet
case User
}
...以及定义requiresComponents
返回数组的方法的协议Components
。
这就是问题出现的地方。为了让“工厂对象”将组件放入Composable
对象中,我们还需要在协议中定义user
和wallet
属性。
protocol Composable {
var user: User? {get set}
var wallet: Wallet? {get set}
func requiresComponents() -> [Component]
}
为了使这些属性“可选”(不是可选的),我定义了Composable
协议的扩展,将这些变量定义为 nil。
extension Composable {
var user: User? {
get {return nil}
set {}
}
var wallet: Wallet? {
get {return nil}
set {}
}
}
现在我声明我要创建的类Composable
。如您所见,它需要User
组件并声明变量。
class SomeComposableClass: Composable {
var user: User?
func requiresComponents() -> [Component] {
return [.User]
}
}
现在FlowController
将创建这些并将组件添加到它们。您可以在这里看到我必须获取对象,创建它的本地var
版本,然后返回更新的对象。我认为这是因为它不知道将符合协议的对象的类型,因此不能改变参数。
class FlowController {
func addComponents<T: Composable>(toComposableObject object: T) -> T {
var localObject = object
for component in object.requiresComponents() {
switch component {
case .Wallet:
localObject.wallet = Wallet()
print("Wallet")
case .User:
localObject.user = User()
print("User")
}
}
return localObject
}
}
我在这里创建对象。
let flowController = FlowController()
let composable = SomeComposableClass()
在这里我添加了组件。在生产中,这将在FlowController
.
flowController.addComponents(toComposableObject: composable) // prints "User" when adding the user component
compassable.user?.sayHello() // prints "Hello, world!"
如您所见,它在这里工作。用户对象被添加。
但是,您也可以看到。因为我已经在协议中声明了变量,所以该composable
对象还具有对wallet
组件的引用(尽管它总是为零)。
composable.wallet // nil
我觉得我已经完成了 95% 的工作,但我想做的是改进属性的声明方式。我想要的是最后一行......composable.wallet
成为编译错误。
我可以通过将属性声明移出协议来做到这一点,但是我遇到了无法将属性添加到任何符合Composable
协议的对象的问题。
令人敬畏的是工厂对象能够在不依赖声明的情况下添加属性。或者甚至有某种守卫说“如果这个对象有一个属性调用用户,那么将用户组件添加到它”。或类似的东西。
如果有人知道我怎样才能让这剩下的 5% 工作,那就太棒了。就像我说的,这行得通,只是不是以理想的方式。
感谢:D
哈基编辑
嗯......作为一个快速俗气,可怕的,“没有人应该这样做”的编辑。我已将我的协议扩展更改为这样......
extension Composable {
var user: User? {
get {fatalError("Access user")}
set {fatalError("Set user")}
}
var wallet: Wallet? {
get {fatalError("Access wallet")}
set {fatalError("Set waller")}
}
}
现在,如果我尝试访问尚未定义的变量,至少程序会崩溃。但它仍然不理想。
阅读丹尼尔的博客后编辑
好吧,我想我已经做到了我想要的。只是不确定它是否完全是 Swifty。虽然,我也觉得有可能。寻求第二意见:)
所以,我的组件和协议变成了这个……
// these are unchanged
class Wallet {
func topUp(amount: Int) {
print("Top up wallet with £\(amount)")
}
}
// each component gets a protocol
protocol WalletComposing {
var wallet: Wallet? {get set}
}
class User {
func sayHello() {
print("Hello, world!")
}
}
protocol UserComposing {
var user: User? {get set}
}
现在工厂方法已经改变了......
// this is the bit I'm unsure about.
// I now have to check for conformance to each protocol
// and add the components accordingly.
// does this look OK?
func addComponents(toComposableObject object: AnyObject) {
if var localObject = object as? UserComposing {
localObject.user = User()
print("User")
}
if var localObject = object as? WalletComposing {
localObject.wallet = Wallet()
print("Wallet")
}
}
这让我可以做到这一点......
class SomeComposableClass: UserComposing {
var user: User?
}
class OtherClass: UserComposing, WalletComposing {
var user: User?
var wallet: Wallet?
}
let flowController = FlowController()
let composable = SomeComposableClass()
flowController.addComponents(toComposableObject: composable)
composable.user?.sayHello()
composable.wallet?.topUp(amount: 20) // this is now a compile time error which is what I wanted :D
let other = OtherClass()
flowController.addComponents(toComposableObject: other)
other.user?.sayHello()
other.wallet?.topUp(amount: 10)