英文:
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("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
?
答案1
得分: 2
替代检查 Entity
参数的方法是,让调用者传入一个工厂函数 () -> T
,handleIncomingRequest
方法使用该工厂函数构造 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 () -> T
, that handleIncomingRequest
uses to construct a T
. This avoids all the as! T
stuff.
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())
// 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) -> 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<T: Content & DomainObject>(request: Request, domainObjectType: T.Type) -> OutboundWrapper<T> {
var domainObject = T()
...
}
Instead of passing LocalSettingsDO.init
, you would pass LocalSettingsDO.self
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论