gpt4 book ai didi

ios - 使用协议(protocol)在 Swift 中构建可组合对象

转载 作者:行者123 更新时间:2023-11-29 00:51:27 34 4
gpt4 key购买 nike

我正在尝试创建一种在 Swift 中构建可比较对象的方法。我觉得我几乎已经掌握了我所拥有的,但它仍然不是 100% 正确。

我的目标是拥有一个 FlowController 对象,它可以创建我们的 UIViewControllers,然后为它们提供所需的任何依赖项。

我还想做的是让这项工作尽可能松散。

我这里有一个小例子,它可以工作但并不理想。我会解释...

这里有两个对象可以用作组件...WalletUser

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
}

...还有一个协议(protocol),它定义了一个方法 requiresComponents,该方法返回一个 Components 数组。

这就是问题出现的地方。为了让“工厂对象”将组件放入 Composable 对象中,我们还需要在协议(protocol)中定义 userwallet 属性.

protocol Composable {
var user: User? {get set}
var wallet: Wallet? {get set}

func requiresComponents() -> [Component]
}

为了使这些属性成为“可选”(非可选),我定义了 Composable 协议(protocol)的扩展,将这些变量定义为 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 版本,然后返回更新后的对象。我认为这是因为它不知道将符合协议(protocol)的对象类型,因此无法更改参数。

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!"

如您所见,它在这里有效。用户对象已添加。

但是,正如您也可以看到的。因为我已经在协议(protocol)中声明了变量,所以可组合对象还具有对钱包组件的引用(尽管它始终为零)。

composable.wallet  // nil

我觉得我已经完成了大约 95%,但我希望能够改进属性的声明方式。我想要的是最后一行...... composable.wallet 是一个编译错误。

我可以通过将属性声明移出协议(protocol)来做到这一点,但随后我遇到了无法将属性添加到符合 Composable 协议(protocol)的任何对象的问题。

如果工厂对象能够在不依赖声明的情况下添加属性,那就太棒了。或者甚至有某种守卫,说“如果这个对象有一个属性调用用户,那么将用户组件添加到它”。或者类似的东西。

如果有人知道我如何让其他 5% 的工作正常进行,那就太棒了。就像我说的,这可行,只是不是以理想的方式。

谢谢:D

黑客编辑

嗯...作为一个快速俗气、可怕的、“没人应该这样做”的编辑。我已经将我的协议(protocol)扩展更改为这样......

extension Composable {
var user: User? {
get {fatalError("Access user")}
set {fatalError("Set user")}
}
var wallet: Wallet? {
get {fatalError("Access wallet")}
set {fatalError("Set waller")}
}
}

现在,如果我尝试访问 undefined variable ,至少程序会崩溃。但仍不理想。

阅读 Daniel 的博客后进行编辑

好吧,我想我已经完成了我想要的。只是不确定它是否正是 Swifty。虽然,我也觉得可能是这样。寻求第二意见:)

所以,我的组件和协议(protocol)变成了这样......

// 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)

最佳答案

这似乎是应用 Interface Segregation Principle 的好例子。

具体来说,不是拥有一个主要的Composable协议(protocol),而是拥有许多较小的协议(protocol),例如UserCompositingWalletCompositing。然后,您希望组成这些不同特征的具体类型,只需列出它们的“requiredComponents”作为它们遵循的协议(protocol),即:

class FlowController : UserComposing, WalletComposing 

我实际上写了一个blog post更广泛地讨论了这一点,并在 http://www.danielhall.io/a-swift-y-approach-to-dependency-injection 中给出了更详细的示例。


更新:

查看更新的问题和示例代码,我仅建议进行以下改进:

回到您最初的设计,定义一个基本的Composition协议(protocol)可能是有意义的,该协议(protocol)要求任何符合标准的类为组合特征创建存储作为字典。像这样:

protocol Composing : class {
var traitDictionary:[String:Any] { get, set }
}

然后,使用协议(protocol)扩展将实际的可组合特征添加为计算属性,这减少了必须在每个一致的类中创建这些属性的样板。这样,任何类都可以符合任意数量的特征协议(protocol),而不必为每个协议(protocol)声明特定的变量。这是一个更完整的示例实现:

class FlowController {
static func userFor(instance:UserComposing) -> User {
return User()
}
static func walletFor(instance:WalletComposing) -> Wallet {
return Wallet()
}
}

protocol Composing : class {
var traitDictionary:[String:Any] { get, set }
}

protocol UserComposing : Composing {}
extension UserComposing {
var user:User {
get {
if let user = traitDictionary["user"] as? User {
return user
}
else {
let user = FlowController.userFor(self)
traitDictionary["user"] = user
return user
}
}
}
}

protocol WalletComposing {}
extension WalletComposing {
var wallet:Wallet {
get {
if let wallet = traitDictionary["wallet"] as? Wallet {
return wallet
}
else {
let wallet = FlowController.walletFor(self)
traitDictionary["wallet"] = wallet
return wallet
}
}
}
}

class AbstractComposing {
var traitDictionary = [String:Any]()
}

这不仅摆脱了那些必须在各处解开的烦人的选项,而且使用户和钱包的注入(inject)隐式且自动。这意味着您的类即使在它们自己的初始化程序中也已经具有这些特征的正确值,无需每次都显式地将每个新实例传递给 FlowController 的实例。

例如,您的最后一个代码片段现在将变得简单:

class SomeComposableClass: AbstractComposing, UserComposing {} // no need to declare var anymore

class OtherClass: AbstractComposing, UserComposing, WalletComposing {} //no vars here either!

let composable = SomeComposableClass() // No need to instantiate FlowController and pass in this instance
composable.user.sayHello() // No unwrapping the optional, this is guaranteed
composable.wallet.topUp(amount: 20) // this is still a compile time error which is what you wanted :D

let other = OtherClass() // No need to instantiate FlowController and pass in this instance
other.user.sayHello()
other.wallet.topUp(amount: 10) // It all "just works" ;)

关于ios - 使用协议(protocol)在 Swift 中构建可组合对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38109915/

34 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com