问题
I'm implementing a model:
- It has structs
ClientSummary
andClientDetails
ClientDetails
struct has all properties ofClientSummary
struct + some extra properties- Both structs have main initializer
init(jsonDictionary: [String: Any])
inits
ofClientSummary
andClientDetails
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