Swift:使用大量属性初始化模型类的正确方法

use*_*176 9 design-patterns initialization optional swift

如何用很多属性初始化类/结构?

如果没有Swift上下文可能会问这个问题,但是Swift给它带来了一些味道,所以我在标题和标签中添加了Swift标签.

假设你有一个User有20个属性的类.他们中的大多数不应该是零或空.让我们假设这些属性不是相互依赖的.让我们假设它的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) {
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 尝试将属性分组为子类型并分别处理较小的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) {
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 另一个解决方案是稍微破坏需求:要么声明一些属性可选,要么在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()
    
    Run Code Online (Sandbox Code Playgroud)

如何处理这种情况是否有一个很好的范式/模式?

Eht*_*san 3

您可以尝试构建器模式。

例子

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)
Run Code Online (Sandbox Code Playgroud)

示例取自此处: 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()
Run Code Online (Sandbox Code Playgroud)

示例取自:http://ctarda.com/2017/09/elegant-swift-default-parameters-vs-the-builder-pattern