iOS 设计模式之建造者模式

360 阅读1分钟

最近在看 Kingfisher 的源码的时候发现它将图片加载改成和 AndroidGlide 一样的加载方式了。

  1. 原来的方式
// 函数
imageView.kf.setImage(
    with: source,
    placeholder: placeholder,
    parsedOptions: options,
    progressBlock: progressBlock,
    completionHandler: resultHandler
)
  1. 现在的方式
KF.url(url)
.setProcessor(currentProcessor)
.serialize(as: .PNG)
.onSuccess { print($0) }
.onFailure { print($0) }
.set(to: cell.cellImageView)

一眼感觉起来还是下面的更舒服。

普通实现

以下代码为直接手敲,可能无法直接运行。

class Test {
    private var p1: Int?
    private var p2: Int?
    // 其他属性...
    private convenience init(_ builder: Builder) {
        self.init()
        p1 = builder.p1
        p2 = builder.p2
        // ...
    }
    
    class Builder {
        var p1: Int?
        var p2: Int?
        
        func setP1(_ p1: Int?) -> Self {
            self.p1 = p1
            return self
        }
        
        func setP2(_ p2: Int?) -> Self {
            self.p2 = p2
            return self
        }
        
        @discardableResult
        func build() -> Test {
            Test(self)
        }
    }
}

// 使用
Test.Builder()
    .setP1(1)
    .setP2(2)
    .build()

KF 的实现

enum KF {
    static func url(_ url: String) -> Builder {
        Builder(url)
    }
    
    class Builder {
        // 若干个属性
        convenience init(_ url: String) {
            self.url = url
        }
    }
}
enum KF.Builder {
    func set(to imageView: UIImageView)  {
        imageView.kf.xxxxx
    }
}
// 其他属性的一些扩展
enum KF.Builder {
    func placeholder(_ string: String) -> Self  {
        self.placeholder = placeholder
        return self
    }
}

实际上这个就是建造者模式的变种。

什么时候使用

  1. 当参数的数量超过 4 个,并且有若干个可选的;
  2. 当所有参数都需要准备完成之后,才可以进行下一步操作

什么时候不需要使用

  1. 当参数较少时,直接使用构造器,或者 set 方法一点毛病也没有。当然考虑扩展性的话,也可以使用。
  2. 当调用函数时不需要所有参数都准备完成时,可以直接变成链式调用,而不需要建造者
let label = Label(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
.text("xxx")
.textColor(.red)

class Label: UILabel {
   func text(_ text: String) -> Self {
       self.text = text
       return self
   }
   
   func textColor(_ textColor: UIColor) -> Self {
       self.textColor = textColor
       return self
   }
}

说实话这样的写法有点 SwiftUI 的感觉。

引用