🏭 类型推断工厂协议系统与宏

这是一个使用 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)
    }
}

使用示例

  1. 导入库
import TypeInferedFactory
  1. 使用工厂创建对象
let factory = TypeInferedFactory()
let user: User = factory.make(1, "Alice")
  1. 在您的类型中实现 TypeInferedFactoryBuildable
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)
    }
}

UserTypeInferedFactoryBuildable 的实现确保了 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。