Swift 泛型函数无需显式声明

huangapple go评论87阅读模式
英文:

Swift Generic Function Without Explicit Declaration

问题

I am facing problem in creating a generic function that will handle all the requests I receive in Vapor. I hope someone can help me to fix this issue

func boot(routes: RoutesBuilder) throws {
    routes.get("localsetting", ":param") { request in
        let response: OutboundWrapper<LocalSettingsDO> = self.handleIncomingRequest(request: request, entity: .localSetting)
        return response
    }
    routes.get("client", ":param") { request in
        let response: OutboundWrapper<ClientDO> = self.handleIncomingRequest(request: request, entity: .client)
        return response
    }
}
fileprivate func handleIncomingRequest<T: Content>(request: Request, entity: Entity) -> OutboundWrapper<T> {
    var outboundWrapper = OutboundWrapper<T>()
    
    switch entity {
    case .client:
        var domainObject = ClientDO()
        if let param = request.parameters.get("param") {
            outboundWrapper = OutboundWrapper(data: domainObject as! T)
        }
    case .localSetting:
        var domainObject = ApplicationLocalSettingsDO()
        if let param = request.parameters.get("param") {
            outboundWrapper = OutboundWrapper(data: domainObject as! T)
        }
    }
    return outboundWrapper
}
struct OutboundWrapper<T: Content>: Content {
    public dynamic var data: T? = nil
    
    public init() {}
    public init(data: T) {
        self.data = data
    }
}

I feel that in this implementation, I missed out the purpose of generic as I need to explicitly cast my function return with let response: OutboundWrapper<ClientDO> for every entity function.

Is there a better way to improve this so that I can just call self.handleIncomingRequest ?

英文:

I am facing problem in creating a generic function that will handle all the requests I receive in Vapor. I hope someone can help me to fix this issue

func boot(routes: RoutesBuilder) throws {
        routes.get(&quot;localsetting&quot;, &quot;:param&quot;) { request in
            let response: OutboundWrapper&lt;LocalSettingsDO&gt; = self.handleIncomingRequest(request: request, entity: .localSetting)
            return response
        }
        routes.get(&quot;client&quot;, &quot;:param:&quot;) { request in
            let response: OutboundWrapper&lt;ClientDO&gt; = self.handleIncomingRequest(request: request, entity: .client)
            return response
        }
}
fileprivate func handleIncomingRequest&lt;T: Content&gt;(request: Request, entity: Entity) -&gt; OutboundWrapper&lt;T&gt; {
        var outboundWrapper = OutboundWrapper&lt;T&gt;()
        
        switch entity {
        case .client:
            var domainObject = ClientDO()
            if let param = request.parameters.get(&quot;param&quot;) {
                outboundWrapper = OutboundWrapper(data: domainObject as! T)
            }
        case .localSetting:
            var domainObject = ApplicationLocalSettingsDO()
            if let param = request.parameters.get(&quot;param&quot;) {
                outboundWrapper = OutboundWrapper(data: domainObject as! T)
            }
        }
        return outboundWrapper
}
struct OutboundWrapper&lt;T: Content&gt;: Content {
    public dynamic var data: T? = nil
    
    public init() {}
    public init(data: T) {
        self.data = data
    }
}

I feel that in this implementation, I missed out the purpose of generic as i need to explicitly cast my function return with let response: OutboundWrapper&lt;ClientDO&gt; for every entity function.

Is there a better way to improve this so that I can just call self.handleIncomingRequest ?

答案1

得分: 2

替代检查 Entity 参数的方法是,让调用者传入一个工厂函数 () -> ThandleIncomingRequest 方法使用该工厂函数构造 T。这样可以避免所有的 as! T 操作。

fileprivate func handleIncomingRequest<T: Content>(request: Request, contentFactory: () -> T) -> OutboundWrapper<T> {

    var domainObject = contentFactory()
    if let param = request.parameters.get("param") {
        let predicate = NSPredicate(format: "ID == %i", param.convertToInt())

        // 对 predicate 进行处理,如果需要的话

        return OutboundWrapper(data: domainObject)
    }
    return OutboundWrapper()
}

用法:

let response = self.handleIncomingRequest(request: request, contentFactory: LocalSettingsDO.init)

或者,可以定义一个要求没有参数的初始化方法的协议:

protocol DomainObject {
    init()

    // 也可以包含其他方法,比如:
    // func predicate(for param: String) -> NSPredicate
}

extension LocalSettingsDO: DomainObject {}
extension ClientDO: DomainObject {}

然后,handleIncomingRequest 方法可以接受一个 T.Type 参数,并且直接在方法内部调用初始化方法:

fileprivate func handleIncomingRequest<T: Content & DomainObject>(request: Request, domainObjectType: T.Type) -> OutboundWrapper<T> {

    var domainObject = T()
    // ...
}

这样,你可以传递 LocalSettingsDO.self 而不是 LocalSettingsDO.init

英文:

Instead of having an Entity parameter that you check, have the caller pass in a factory () -&gt; T, that handleIncomingRequest uses to construct a T. This avoids all the as! T stuff.

fileprivate func handleIncomingRequest&lt;T: Content&gt;(request: Request, contentFactory: () -&gt; T) -&gt; OutboundWrapper&lt;T&gt; {

    var domainObject = contentFactory()
    if let param = request.parameters.get(&quot;param&quot;) {
        let predicate = NSPredicate(format: &quot;ID == %i&quot;, param.convertToInt())

        // do something with predicate, perhaps (?)

        return OutboundWrapper(data: domainObject)
    }
    return OutboundWrapper()
}

Usage:

let response = self.handleIncomingRequest(request: request, contentFactory: LocalSettingsDO.init)

Alternatively, have a protocol that requires a parameterless initialiser:

protocol DomainObbject {
    init()

    // perhaps also:
    // func predicate(for param: String) -&gt; NSPredicate
}

extension LocalSettingsDO: DomainObject {}
extension ClientDO: DomainObject {}

Then handleIncomingRequest can take a T.Type, and you can directly call the initialiser in the method:

fileprivate func handleIncomingRequest&lt;T: Content &amp; DomainObject&gt;(request: Request, domainObjectType: T.Type) -&gt; OutboundWrapper&lt;T&gt; {

    var domainObject = T()
    ...
}

Instead of passing LocalSettingsDO.init, you would pass LocalSettingsDO.self.

huangapple
  • 本文由 发表于 2023年3月8日 16:26:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/75670764.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定