【问题标题】:Swift: The proper way to initialize model class with a lot of propertiesSwift:用大量属性初始化模型类的正确方法
【发布时间】:2017-02-13 10:23:42
【问题描述】:

如何使用大量属性初始化类/结构?

这个问题可能在没有 Swift 上下文的情况下被问到,但 Swift 给它带来了一种味道,所以我在标题和标签中添加了 Swift 标签。

假设您有一个具有 20 个属性的 User 类。它们中的大多数不应为 nil 或为空。让我们假设这些属性不是相互依赖的。让我们假设根据类的逻辑,它的 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) {
        }
    }
    
  • 尝试将属性分组为子类型并分别处理较小的初始化

    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()
    

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

【问题讨论】:

  • 你可以使用Builder模式:stackoverflow.com/questions/33106041/…
  • 没有好的选择 =/。我喜欢 .net 提供内置初始化程序的解决方案。看起来像这样:className{prop1=1,prop2=2}。

标签: swift design-patterns initialization optional


【解决方案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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-18
    • 1970-01-01
    • 2013-12-16
    • 1970-01-01
    • 2012-08-30
    • 2019-10-14
    相关资源
    最近更新 更多