如何使用大量属性初始化类/结构?
这个问题可能会在没有Swift上下文的情况下被问到,但是Swift带来了它的味道,所以我在标题和标签中添加了Swift标签。
假设您有一个包含20个属性的User
类。他们中的大多数不应该是零或空。让我们假设这些属性不是相互依赖的。让我们假设它的33%应该是类的逻辑不变(let
)。假设它们中至少有65%没有有意义的默认值。你将如何设计这个类并初始化它的实例?
到目前为止,我几乎没有什么想法,但似乎没有一件事让我完全满意:
将所有属性线性地放在类中并制作巨大的init方法:
class User {
// there is 20 properties like that
let id : String
let username : String
let email : String
...
var lastLoginDate : Date
var lastPlayDate : Date
// then HUUUUGE init
init(id: String,
username: String,
...
lastPlayDate: Date) {
}
}
尝试将属性分组为子类型并分别处理较小的inits
class User {
struct ID {
let id : String
let username : String
let email : String
}
struct Activity {
var lastLoginDate : Date
var lastPlayDate : Date
}
let id : ID
...
var lastActivity : Activity
// then not so huge init
init(id: ID,
...
lastActivity: Activity) {
}
}
另一个解决方案是稍微破坏需求:要么声明一些属性可选,要么在init之后设置值或声明虚拟默认值并在init之后设置正常值,这在概念上似乎是相同的
class User {
// there is 20 properties like that
let id : String
let username : String
let email : String
...
var lastLoginDate : Date?
var lastPlayDate : Date?
// then not so huge init
init(id: String,
username: String,
email: String) {
}
}
// In other code
var user = User(id: "1", username: "user", email: "user@example.com"
user.lastLoginDate = Date()
是否有一个很好的范例/模式来处理这种情况?
答案 0 :(得分:1)
您可以尝试使用构建器模式。
class DeathStarBuilder {
var x: Double?
var y: Double?
var z: Double?
typealias BuilderClosure = (DeathStarBuilder) -> ()
init(buildClosure: BuilderClosure) {
buildClosure(self)
}
}
struct DeathStar : CustomStringConvertible {
let x: Double
let y: Double
let z: Double
init?(builder: DeathStarBuilder) {
if let x = builder.x, let y = builder.y, let z = builder.z {
self.x = x
self.y = y
self.z = z
} else {
return nil
}
}
var description:String {
return "Death Star at (x:\(x) y:\(y) z:\(z))"
}
}
let empire = DeathStarBuilder { builder in
builder.x = 0.1
builder.y = 0.2
builder.z = 0.3
}
let deathStar = DeathStar(builder:empire)
从这里取得的例子:https://github.com/ochococo/Design-Patterns-In-Swift
如果您正在寻找更像Java的解决方案,您可以尝试这样的事情。
final class NutritionFacts {
private let servingSize: Int
private let servings: Int
private let calories: Int
private let fat: Int
private let sodium: Int
private let carbs: Int
init(builder: Builder) {
servingSize = builder.servingSize
servings = builder.servings
calories = builder.calories
fat = builder.fat
sodium = builder.sodium
carbs = builder.carbs
}
class Builder {
let servingSize: Int
let servings: Int
private(set) var calories = 0
private(set) var fat = 0
private(set) var carbs = 0
private(set) var sodium = 0
init(servingSize: Int, servings: Int) {
self.servingSize = servingSize
self.servings = servings
}
func calories(value: Int) -> Builder {
calories = value
return self
}
func fat(value: Int) -> Builder {
fat = value
return self
}
func carbs(value: Int) -> Builder {
carbs = value
return self
}
func sodium(value: Int) -> Builder {
sodium = value
return self
}
func build() -> NutritionFacts {
return NutritionFacts(builder: self)
}
}
}
let facts = NutritionFacts.Builder(servingSize: 10, servings: 1)
.calories(value: 20)
.carbs(value: 2)
.fat(value: 5)
.build()
示例摘自:http://ctarda.com/2017/09/elegant-swift-default-parameters-vs-the-builder-pattern