【问题标题】:How to access inner object's properties from superclass without boilerplate如何在没有样板的情况下从超类访问内部对象的属性
【发布时间】:2019-06-27 01:56:35
【问题描述】:

有没有办法在没有样板的情况下创建内部对象属性的访问器?

这是一个带有样板的类的示例

public class Foo {
    internal let bar: Bar

    internal init(bar: Bar) {
        self.bar = bar
    }

    private struct Bar: Codable {
        let id: Int
        let name: String
    }  

    // MARK: - Boilerplate 

    public var id: Int { 
        return self.bar.id
    }

    public var name: String { 
        return self.bar.name
    }
}

// Usage

do {
    let bar: Bar = try data.decoded() 
    let foo = Foo(bar: bar)

    print(foo.id)
    print(foo.name)
} catch {
    ...
}

有没有办法做到这一点而无需编写样板文件?对于具有更多属性的较大对象,这可能真的很有帮助

注意:访问控制修饰符很重要

【问题讨论】:

  • 你怎么能在Foo之外let bar : Bar = try data.decoded()Bar 不是 Foo 中的私有吗?
  • 你好@RickyMo 这是在同一个代码库中,在项目之外这是不可能的。这个问题是出于封装目的和单一责任
  • 在 Swift 5.1 中,您可以进行“动态成员查找”,比较 stackoverflow.com/q/56771120/1187415
  • 让我试试那个。
  • @MartinR 是的,我刚刚也独立地意识到了这一点,并在我的回答中添加了一个关于它的注释。好点。

标签: swift encapsulation abstraction


【解决方案1】:

Swift 5.1 中的新功能,您可以使用带有键路径的动态成员查找。这对于这种“有”的情况非常有效,因为关键路径保持完整的类型检查。这是一个简化的示例(没有试图反映您的实际情况):

struct Dog {
    let name : String
}
@dynamicMemberLookup
struct Kennel {
    let dog : Dog
    subscript(dynamicMember kp:KeyPath<Dog,String>) -> String {
        self.dog[keyPath:kp]
    }
}

结果是,给定一个犬舍k,我们可以得到k.name 作为获取k.dog.name 的一种方式。

但是说 k.xxx 是非法的,因为 Dog 没有字符串 xxx 属性;这就是我所说的保持完整类型检查的意思。


旧方法是使用协议。这样您就可以使用协议扩展来注入样板。

protocol HasNameAndId {
    var id: Int {get}
    var name: String {get}
}

protocol WrapperOfHasNameAndId {
    associatedtype T : HasNameAndId
    var bar: T {get}
}

extension WrapperOfHasNameAndId { // boilerplate
    var id: Int {
        return self.bar.id
    }
    var name: String {
        return self.bar.name
    }
}

// ==============

struct Bar: HasNameAndId {
    let id: Int
    let name: String
}

class Foo : WrapperOfHasNameAndId {
    let bar: Bar
    init(bar: Bar) {
        self.bar = bar
    }
}

// =======

let f = Foo(bar: Bar(id: 1, name: "howdy"))
print(f.id) // 1
print(f.name) // "howdy"

【讨论】:

  • 我想这是最好的解决方案,我必须编写代码,但至少样板文件在其他地方。但它仍然不起作用,因为我需要 extension WrapperOfHasNameAndId 公开,我 protocol WrapperOfHasNameAndId 需要在 internal 范围内。我想没有办法解决这个问题吗?
  • 好吧,在 Swift 5.1 中,有一种全新的方法可以做到这一点。哦,我看到 Martin R 在评论中指出了这一点。在我的回答中添加了一些关于此的内容。我想这是你想直接进去的地方。
  • 如果我没记错的话,在 WWDC2019 的一次会议演讲中有一个很好的例子。它是关于实现 COW 值类型,并将属性转发到内部引用类型。
  • @MartinR 是的,那是会话 415。但是,此功能通常作为自动转发到包装对象的一种方式很有价值。不幸的是,没有自动方法调用转发的模拟,虽然我不明白为什么他们最终不能以某种方式添加它。
  • 我明白了,很遗憾我要求的这个功能仍然不可用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-17
  • 1970-01-01
  • 2018-05-27
  • 1970-01-01
  • 1970-01-01
  • 2023-01-25
  • 1970-01-01
相关资源
最近更新 更多