How to use protocols for stucts to emulate classes inheritance

扶醉桌前 提交于 2021-02-08 06:38:33

问题


I'm implementing a model:

  • It has structs ClientSummary and ClientDetails
  • ClientDetails struct has all properties of ClientSummary struct + some extra properties
  • Both structs have main initializer init(jsonDictionary: [String: Any])
  • inits of ClientSummary and ClientDetails share big part of the code
  • There is an extension which will work with shared functionality of those structs.

The most straightforward solution which came to my mind is just classic inheritance, but it doesn't work for value types.

I'm trying to solve that with protocols, but I can't implement those "shared inits". I was trying to move shared part of the init to the protocol extension but can't really make it. There are various errors.

Here is the test code.

protocol Client {
    var name: String { get }
    var age: Int { get }
    var dateOfBirth: Date { get }

    init?(jsonDictionary: [String: Any])
}


struct ClientSummary: Client {
    let name: String
    let age: Int
    let dateOfBirth: Date

    init?(jsonDictionary: [String: Any]) {
        guard let name = jsonDictionary["name"] as? String else {
            return nil
        }
        self.name = name
        age = 1
        dateOfBirth = Date()
    }
}

struct ClientDetails: Client {
    let name: String
    let age: Int
    let dateOfBirth: Date
    let visitHistory: [Date: String]?

    init?(jsonDictionary: [String: Any]) {
        guard let name = jsonDictionary["name"] as? String else {
            return nil
        }
        self.name = name
        age = 1
        dateOfBirth = Date()
        visitHistory = [Date(): "Test"]
    }
}

extension Client {
    // A lot of helper methods here
    var stringDOB: String {
        return formatter.string(from: dateOfBirth)
    }
}

回答1:


Inheritance is the wrong tool here. It doesn't make sense to say "details IS-A summary." Details are not a kind of summary. Step away from the structural question of whether they share a lot of methods, and focus on the essential question of whether one is a kind of the other. (Sometimes renaming things can make that true, but as long as they're "summary" and "detail" it doesn't make sense to inherit.)

What can make sense is to say that details HAS-A summary. Composition, not inheritance. So you wind up with something like:

struct ClientDetails {
    let summary: ClientSummary
    let visitHistory: [Date: String]?

    init?(jsonDictionary: [String: Any]) {
        guard let summary = ClientSummary(jsonDictionary: jsonDictionary) else {
            return nil
        }
        self.summary = summary
        visitHistory = [Date(): "Test"]
    }

    // You can add these if you need them, or to conform to Client if that's still useful.
    var name: String { return summary.name }
    var age: Int { return summary.age }
    var dateOfBirth: Date { return summary.dateOfBirth }
}



回答2:


I often wish that Swift had a built-in way to separate out parts of init methods. However, it can be done, admittedly somewhat awkwardly, with tuples, as below:

struct S {
    let foo: String
    let bar: Int
    let baz: Bool

    init() {
        (self.foo, self.bar, self.baz) = S.sharedSetup()
    }

    static func sharedSetup() -> (String, Int, Bool) {
        ...
    }
}

In your case, the sharedSetup() method can be moved to the protocol extension, or wherever it's convenient to have it.



来源:https://stackoverflow.com/questions/45868623/how-to-use-protocols-for-stucts-to-emulate-classes-inheritance

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!