You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift多实例间共享Builder类的实践疑问:单例是否可行?

Builder模式单例化的实践疑问解答

问题背景

我正在提升开发能力,开始在小型测试项目中学习使用设计模式。当前项目聚焦Builder模式,包含两个Storyboard视图、一个被所有视图复用的ViewController类,以及一个在ViewController中实现、用于创建含两个属性对象的Builder类。

代码示例:

class ViewController: UIViewController {
    @IBOutlet var startTimePicker: UIDatePicker!
    @IBOutlet var workingHoursPicker: UIDatePicker!

    @IBAction func setStartTimeButtonPressed(_ sender: Any) {
        setStartTime()
    }
    
    @IBAction func setWorkingHoursButtonPressed(_ sender: Any) {
        setWorkingHours()
    }
    
    var workdayBuilder: WorkdayBuilder = ConcreteWorkdayBuilder()
    // ... 其他代码

当前问题:每次通过Segue跳转视图时,ViewController会被重新实例化,导致之前设置的Builder值丢失。我知晓可通过传值或传递Builder实例解决,但认为这会破坏本项目中Builder模式的设计初衷。

于是尝试将Builder包装成单例以实现多视图共享:

final class WorkdayBuilderSingleton { 
    static let sharedInstance: WorkdayBuilder = ConcreteWorkdayBuilder() 
    init() {} 
}

但在ViewController中设置属性时遇到错误:Cannot assign to property: 'sharedInstance' is a 'let' constant。

现存在疑问:

  1. 将Builder类设计为单例是否属于可行的良好实践?
  2. 若可行,能否将static let改为static var而不破坏单例特性?

解答

1. Builder类设计为单例是否是良好实践?

得结合你的业务场景判断:

  • 如果你的Workday对象是全局唯一的、贯穿整个流程的上下文(比如用户正在构建的当日工作配置,整个APP生命周期内只需要维护一份),用单例持有Builder是可行的,但要注意两个局限:
    • Builder模式的核心是分步构建不同配置的对象,单例会让Builder的状态全局共享,意味着你无法同时构建多个不同的Workday实例,直接限制了Builder的灵活性。
    • 单例的状态是全局可变的,容易引发意料之外的副作用(比如某个页面修改了Builder的属性,其他页面会直接感知到,调试时会增加复杂度)。
  • 如果你只是想在多个ViewController间共享Builder的状态,其实更推荐用依赖注入或者通过Segue传递Builder实例——这并不会破坏Builder模式的初衷,反而符合单一职责:Builder负责构建对象,视图控制器负责收集用户输入并调用Builder,传递实例只是在不同视图间共享构建上下文而已。

2. 能否将static let改为static var而不破坏单例特性?

可以,但要注意正确的实现方式:

  • 直接把static let改成static var会让外部代码直接重新赋值sharedInstance,彻底破坏单例的唯一性(比如其他地方可以写WorkdayBuilderSingleton.sharedInstance = AnotherBuilder())。
  • 正确的做法是把sharedInstance设为private(set) static var,这样外部只能读取实例、修改实例内部属性,不能重新赋值整个实例:
final class WorkdayBuilderSingleton { 
    // 外部只能读取实例,不能重新赋值整个对象
    private(set) static var sharedInstance: WorkdayBuilder = ConcreteWorkdayBuilder() 
    // 私有化构造器,防止外部手动实例化
    private init() {} 
}

你之前遇到的错误,本质是试图重新给sharedInstance这个常量赋值,而不是修改它内部的属性。改成上面的写法后,就可以正常调用WorkdayBuilderSingleton.sharedInstance.setStartTime(...)这类方法修改内部状态了。

另外,如果你不需要全局唯一的Builder,只是跨页面共享状态,也可以考虑用ViewModel层持有Builder,通过Segue传递ViewModel实例,这样既隔离了状态,又比单例更灵活。


内容的提问来源于stack exchange,提问作者bkDev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 15:00:58