这是一个使用 Swift 参数包的实验。其核心思想是创建一个协议系统,使工厂(或类似工厂的东西)及其生产的对象更易于管理。
这个类工厂实现的的核心思想是将对象创建细节封装在工厂内部,防止直接暴露实现细节。为了实现这一点,我设计了两个协议来解决这个问题。
1 - TypeInferedFactoryBuildable
一个协议,用于可以使用工厂方法构建的类型。
public protocol TypeInferedFactoryBuildable {
associatedtype RequiredInitializationParameter
static func construct(_ parameter: RequiredInitializationParameter) -> Self
}
采用此协议的类型声明其所需的初始化参数,并提供一个用于对象创建的 construct 方法。
2 - TypeInferedFactoryProtocol
一个协议,用于创建符合 TypeInferedFactoryBuildable
的对象的工厂。
public protocol TypeInferedFactoryProtocol {
func make<Output, each T>(
_ value: repeat each T
) -> Output where Output: TypeInferedFactoryBuildable, Output.RequiredInitializationParameter == (repeat each T)
}
此协议定义了一个 make 方法,使用提供的值动态构建一个输出对象。
为了支持此工厂系统,提供了一个基类 Factory。此类实现了 TypeInferedFactoryProtocol 并且可以被重写以实现自定义行为
open class TypeInferedFactory: TypeInferedFactoryProtocol {
public init() {}
public func make<Output, each T>(
_ value: repeat each T
) -> Output where Output: TypeInferedFactoryBuildable, Output.RequiredInitializationParameter == (repeat each T) {
let tuple = (repeat each value)
return Output.construct(tuple)
}
}
import TypeInferedFactory
let factory = TypeInferedFactory()
let user: User = factory.make(1, "Alice")
struct User {
let id: Int
let name: String
}
extension User: TypeInferedFactoryBuildable {
typealias RequiredInitializationParameter = (Int, String)
static func construct(_ parameter: RequiredInitializationParameter) -> User {
return User(id: parameter.0, name: parameter.1)
}
}
User
对 TypeInferedFactoryBuildable
的实现确保了 make
方法内部参数包的编译时安全性。 如果传递给 make
方法的任何参数与 RequiredInitializationParameter 元组不同,Swift 编译器将抛出错误。
对于更复杂的类型,手动实现 construct
方法可能会变得繁琐
import TypeInferedFactory
final class SimpleContainer {
let firstValue: Int
let secondValue: String
let description: String
init(firstValue: Int, secondValue: String, description: String, shouldRedact: Bool) {
self.firstValue = shouldRedact ? -1 : firstValue
self.secondValue = shouldRedact ? "" : secondValue
self.description = shouldRedact ? "" : description
}
convenience init(firstValue: Int, secondValue: String) {
self.init(firstValue: firstValue, secondValue: secondValue, description: "Default description", shouldRedact: false)
}
convenience init(firstValue: Int) {
self.init(firstValue: firstValue, secondValue: "Default String", description: "Default description", shouldRedact: false)
}
}
extension SimpleContainer: TypeInferedFactoryBuildable {
typealias RequiredInitializationParameter = (Int, String, String, Bool)
static func construct(_ parameter: RequiredInitializationParameter) -> SimpleContainer {
SimpleContainer(
firstValue: parameter.0,
secondValue: parameter.1,
description: parameter.2,
shouldRedact: parameter.3
)
}
}
RequiredInitializationParameter
元组反映了参数数量最多的初始值设定项的所有参数。在复杂的类中处理这些元组索引可能会变得乏味。
为了简化这个过程,包中包含了一个 Swift 宏。 它可以自动生成 TypeInferedFactoryBuildable
一致性所需的代码。
输入
import TypeInferedFactory
@FactoryBuildable
struct User {
let id: Int
let name: String
}
输出(生成的代码)
extension User: TypeInferedFactoryBuildable {
typealias RequiredInitializationParameter = (Int, String)
static func construct(_ parameter: RequiredInitializationParameter) -> User {
return User(id: parameter.0, name: parameter.1)
}
}
该宏评估其目标。 如果目标没有任何初始值设定项,它会根据该类型的属性生成 RequiredInitializationParameter
。
输入
import TypeInferedFactory
@FactoryBuildable
final class SimpleContainer {
let firstValue: Int
let secondValue: String
let description: String
init(firstValue: Int, secondValue: String, description: String, shouldRedact: Bool) {
self.firstValue = shouldRedact ? -1 : firstValue
self.secondValue = shouldRedact ? "" : secondValue
self.description = shouldRedact ? "" : description
}
convenience init(firstValue: Int, secondValue: String) {
self.init(firstValue: firstValue, secondValue: secondValue, description: "Default description", shouldRedact: false)
}
convenience init(firstValue: Int) {
self.init(firstValue: firstValue, secondValue: "Default String", description: "Default description", shouldRedact: false)
}
}
输出(生成的代码)
extension SimpleContainer: TypeInferedFactoryBuildable {
typealias RequiredInitializationParameter = (Int, String, String, Bool)
static func construct(_ parameter: RequiredInitializationParameter) -> SimpleContainer {
SimpleContainer(firstValue: parameter.0, secondValue: parameter.1, description: parameter.2, shouldRedact: parameter.3)
}
}
该宏基于参数数量最多的初始化程序生成 RequiredInitializationParameter
。
这尚未准备好用于生产环境,但您可以使用 Swift Package Manager (SPM) 安装它。
将包添加到您的 Package.swift
dependencies: [
.package(url: "https://github.com/your-repo/TypeInferedFactoryMacro.git", from: "0.0.1")
]
非 final 类:由于 TypeInferedFactoryBuildable
的 construct 方法返回 Self
,因此此实现仅支持结构体和 final 类。 非 final 类不能可靠地使用 Self.init,因为编译器无法保证子类的正确初始化,这可能会导致运行时错误或编译问题。 常见的编译器错误包括
对于非 final 类,您必须手动实现 TypeInferedFactoryBuildable。
实验状态:该宏尚未经过全面测试。 不保证它与属性包装器、成员宏和结果构建器初始化程序的行为。 如果发生不希望的代码生成,请手动实现 TypeInferedFactoryBuildable。