生成器模式
构建器模式是对象创建软件设计模式。与抽象工厂模式和旨在实现多态的工厂方法模式不同,构建器模式的目的是找到伸缩构造函数反模式的解决方案。当对象构造函数参数组合的增加导致构造函数的指数列表时,发生伸缩构造函数反模式。构建器模式不是使用大量构造函数,而是使用另一个对象(构建器),它逐步接收每个初始化参数,然后立即返回生成的构造对象。
构建器模式的主要目标是为对象的创建设置默认配置。它是建立对象和与构建它相关的所有其他对象之间的中介。
例:
为了更清楚,让我们来看一下 Car Builder 的例子。
考虑到我们有一个 Car 类包含许多创建对象的选项,例如:
- 颜色。
- 座位数量。
- 车轮数量。
- 类型。
- 齿轮类型。
- 发动机。
- 安全气囊可用性。
import UIKit
enum CarType {
case
sportage,
saloon
}
enum GearType {
case
manual,
automatic
}
struct Motor {
var id: String
var name: String
var model: String
var numberOfCylinders: UInt8
}
class Car: CustomStringConvertible {
var color: UIColor
var numberOfSeats: UInt8
var numberOfWheels: UInt8
var type: CarType
var gearType: GearType
var motor: Motor
var shouldHasAirbags: Bool
var description: String {
return "color: \(color)\nNumber of seats: \(numberOfSeats)\nNumber of Wheels: \(numberOfWheels)\n Type: \(gearType)\nMotor: \(motor)\nAirbag Availability: \(shouldHasAirbags)"
}
init(color: UIColor, numberOfSeats: UInt8, numberOfWheels: UInt8, type: CarType, gearType: GearType, motor: Motor, shouldHasAirbags: Bool) {
self.color = color
self.numberOfSeats = numberOfSeats
self.numberOfWheels = numberOfWheels
self.type = type
self.gearType = gearType
self.motor = motor
self.shouldHasAirbags = shouldHasAirbags
}
}
创建汽车对象:
let aCar = Car(color: UIColor.black,
numberOfSeats: 4,
numberOfWheels: 4,
type: .saloon,
gearType: .automatic,
motor: Motor(id: "101", name: "Super Motor",
model: "c4", numberOfCylinders: 6),
shouldHasAirbags: true)
print(aCar)
/* Printing
color: UIExtendedGrayColorSpace 0 1
Number of seats: 4
Number of Wheels: 4
Type: automatic
Motor: Motor(id: "101", name: "Super Motor", model: "c4", numberOfCylinders: 6)
Airbag Availability: true
*/
创建汽车对象时出现的问题是汽车需要创建许多配置数据。
对于应用构建器模式,初始化程序参数应具有默认值*,如果需要,可以更改这些*值。
CarBuilder 类:
class CarBuilder {
var color: UIColor = UIColor.black
var numberOfSeats: UInt8 = 5
var numberOfWheels: UInt8 = 4
var type: CarType = .saloon
var gearType: GearType = .automatic
var motor: Motor = Motor(id: "111", name: "Default Motor",
model: "T9", numberOfCylinders: 4)
var shouldHasAirbags: Bool = false
func buildCar() -> Car {
return Car(color: color, numberOfSeats: numberOfSeats, numberOfWheels: numberOfWheels, type: type, gearType: gearType, motor: motor, shouldHasAirbags: shouldHasAirbags)
}
}
CarBuilder
类定义可以更改为的属性,以编辑创建的 car 对象的值。
让我们使用 CarBuilder
构建新车:
var builder = CarBuilder()
// currently, the builder creates cars with default configuration.
let defaultCar = builder.buildCar()
//print(defaultCar.description)
/* prints
color: UIExtendedGrayColorSpace 0 1
Number of seats: 5
Number of Wheels: 4
Type: automatic
Motor: Motor(id: "111", name: "Default Motor", model: "T9", numberOfCylinders: 4)
Airbag Availability: false
*/
builder.shouldHasAirbags = true
// now, the builder creates cars with default configuration,
// but with a small edit on making the airbags available
let safeCar = builder.buildCar()
print(safeCar.description)
/* prints
color: UIExtendedGrayColorSpace 0 1
Number of seats: 5
Number of Wheels: 4
Type: automatic
Motor: Motor(id: "111", name: "Default Motor", model: "T9", numberOfCylinders: 4)
Airbag Availability: true
*/
builder.color = UIColor.purple
// now, the builder creates cars with default configuration
// with some extra features: the airbags are available and the color is purple
let femaleCar = builder.buildCar()
print(femaleCar)
/* prints
color: UIExtendedSRGBColorSpace 0.5 0 0.5 1
Number of seats: 5
Number of Wheels: 4
Type: automatic
Motor: Motor(id: "111", name: "Default Motor", model: "T9", numberOfCylinders: 4)
Airbag Availability: true
*/
应用 Builder Pattern 的好处是,通过设置默认值,以及更改这些默认值的简易性,可以轻松创建应包含大量配置的对象。
进一步:
作为一种好的做法,所有需要默认值的属性都应该在一个单独的协议中,该协议应该由类本身及其构建器实现。
回到我们的例子,让我们创建一个名为 CarBluePrint
的新协议:
import UIKit
enum CarType {
case
sportage,
saloon
}
enum GearType {
case
manual,
automatic
}
struct Motor {
var id: String
var name: String
var model: String
var numberOfCylinders: UInt8
}
protocol CarBluePrint {
var color: UIColor { get set }
var numberOfSeats: UInt8 { get set }
var numberOfWheels: UInt8 { get set }
var type: CarType { get set }
var gearType: GearType { get set }
var motor: Motor { get set }
var shouldHasAirbags: Bool { get set }
}
class Car: CustomStringConvertible, CarBluePrint {
var color: UIColor
var numberOfSeats: UInt8
var numberOfWheels: UInt8
var type: CarType
var gearType: GearType
var motor: Motor
var shouldHasAirbags: Bool
var description: String {
return "color: \(color)\nNumber of seats: \(numberOfSeats)\nNumber of Wheels: \(numberOfWheels)\n Type: \(gearType)\nMotor: \(motor)\nAirbag Availability: \(shouldHasAirbags)"
}
init(color: UIColor, numberOfSeats: UInt8, numberOfWheels: UInt8, type: CarType, gearType: GearType, motor: Motor, shouldHasAirbags: Bool) {
self.color = color
self.numberOfSeats = numberOfSeats
self.numberOfWheels = numberOfWheels
self.type = type
self.gearType = gearType
self.motor = motor
self.shouldHasAirbags = shouldHasAirbags
}
}
class CarBuilder: CarBluePrint {
var color: UIColor = UIColor.black
var numberOfSeats: UInt8 = 5
var numberOfWheels: UInt8 = 4
var type: CarType = .saloon
var gearType: GearType = .automatic
var motor: Motor = Motor(id: "111", name: "Default Motor",
model: "T9", numberOfCylinders: 4)
var shouldHasAirbags: Bool = false
func buildCar() -> Car {
return Car(color: color, numberOfSeats: numberOfSeats, numberOfWheels: numberOfWheels, type: type, gearType: gearType, motor: motor, shouldHasAirbags: shouldHasAirbags)
}
}
将需要默认值的属性声明为协议的好处是强制实现任何新添加的属性; 当一个类符合协议时,它必须声明其所有属性/方法。
考虑到应该将新功能添加到创建名为电池名称的汽车的蓝图中:
protocol CarBluePrint {
var color: UIColor { get set }
var numberOfSeats: UInt8 { get set }
var numberOfWheels: UInt8 { get set }
var type: CarType { get set }
var gearType: GearType { get set }
var motor: Motor { get set }
var shouldHasAirbags: Bool { get set }
// adding the new property
var batteryName: String { get set }
}
添加新属性后,请注意会出现两个编译时错误,通知符合 CarBluePrint
协议需要声明’batteryName’属性。这保证了 CarBuilder
将声明并设置 batteryName
属性的默认值。
将 batteryName
新属性添加到 CarBluePrint
协议后,Car
和 CarBuilder
类的实现应该是:
class Car: CustomStringConvertible, CarBluePrint {
var color: UIColor
var numberOfSeats: UInt8
var numberOfWheels: UInt8
var type: CarType
var gearType: GearType
var motor: Motor
var shouldHasAirbags: Bool
var batteryName: String
var description: String {
return "color: \(color)\nNumber of seats: \(numberOfSeats)\nNumber of Wheels: \(numberOfWheels)\nType: \(gearType)\nMotor: \(motor)\nAirbag Availability: \(shouldHasAirbags)\nBattery Name: \(batteryName)"
}
init(color: UIColor, numberOfSeats: UInt8, numberOfWheels: UInt8, type: CarType, gearType: GearType, motor: Motor, shouldHasAirbags: Bool, batteryName: String) {
self.color = color
self.numberOfSeats = numberOfSeats
self.numberOfWheels = numberOfWheels
self.type = type
self.gearType = gearType
self.motor = motor
self.shouldHasAirbags = shouldHasAirbags
self.batteryName = batteryName
}
}
class CarBuilder: CarBluePrint {
var color: UIColor = UIColor.red
var numberOfSeats: UInt8 = 5
var numberOfWheels: UInt8 = 4
var type: CarType = .saloon
var gearType: GearType = .automatic
var motor: Motor = Motor(id: "111", name: "Default Motor",
model: "T9", numberOfCylinders: 4)
var shouldHasAirbags: Bool = false
var batteryName: String = "Default Battery Name"
func buildCar() -> Car {
return Car(color: color, numberOfSeats: numberOfSeats, numberOfWheels: numberOfWheels, type: type, gearType: gearType, motor: motor, shouldHasAirbags: shouldHasAirbags, batteryName: batteryName)
}
}
再次,让我们使用 CarBuilder
构建新车:
var builder = CarBuilder()
let defaultCar = builder.buildCar()
print(defaultCar)
/* prints
color: UIExtendedSRGBColorSpace 1 0 0 1
Number of seats: 5
Number of Wheels: 4
Type: automatic
Motor: Motor(id: "111", name: "Default Motor", model: "T9", numberOfCylinders: 4)
Airbag Availability: false
Battery Name: Default Battery Name
*/
builder.batteryName = "New Battery Name"
let editedBatteryCar = builder.buildCar()
print(editedBatteryCar)
/*
color: UIExtendedSRGBColorSpace 1 0 0 1
Number of seats: 5
Number of Wheels: 4
Type: automatic
Motor: Motor(id: "111", name: "Default Motor", model: "T9", numberOfCylinders: 4)
Airbag Availability: false
Battery Name: New Battery Name
*/