在基类使用泛型斯威夫特容易地返回类型类的子类

问题描述

我想在 BasePresenter 类中保留一个 Interactor 类,但对于我的生活,我无法弄清楚到底如何在 Swift 中轻松使用泛型。我想在基类中有一个函数(比如 BasePresenter interactorForType)。

public class InBoxListPresenter: BasePresenter,ObservableObject {
        
    public init(interactor: InBoxListInteractor) {
        
        super.init(router: InBoxListRouter(viewData: viewData),interactor: interactor)
    }
    
    func findInBoxNotifications() {
        
        interactorForType(type: InBoxListInteractor.self).findInBoxNotifications() { inBoxNotifications,errorCode in
            
            // do something
        }
    }    
}

这是我想要保存通用对象的 BasePresenter。我正在尝试使用协议 (ILibertyInteractor)

open class BasePresenter {
    
    public let router:ILibertyRouter
    public let interactor:ILibertyInteractor
    
    public init(router:ILibertyRouter,interactor:ILibertyInteractor) {
        self.router = router
        self.interactor = interactor
    }
            
    func interactorForType<T>(type: T.Type) -> T {
        return interactor as! T
    }
    
    func routerForType<T>(type: T.Type) -> T {
        return router as! T
    }
}

上面的interactorForType 对我想要避免的交互强制展开。另外,我也不想传入对象。我想使用的协议和泛型有一个通用的函数,返回类型T

下面是我试图允许在上面的演示者中返回类型的开始。

public protocol ILibertyPresenter {
    var router:ILibertyRouter { get }
}

public protocol ILibertyRouter {
    
}

public protocol ILibertyInteractor {
//    associatedtype T
    
//    func trueInteractor() -> T
}

解决方法

这里有两件事。您会问“如何存储泛型?”的问题,然后是您的示例代码似乎要尝试执行的操作。

如何存储通用值

关于如何存储泛型(假设通过 Any 是不可取的 - 而且是不可取的)的简短答案是将其存储在泛型中。

如果想要保留泛型类型作为其显式类型,那么保留它的东西也必须是泛型的并且专门针对同一类型。这也需要您的 InboxListPresenter 是通用的。所以你可以这样做:

open class BasePresenter <InteractorType: IteractorProtocol>{
    
    public let router:ILibertyRouter
    public let interactor: InteractorType // You can do this now
    
    public init(router:ILibertyRouter,interactor:ILibertyInteractor) {
        self.router = router
        self.interactor = interactor
    }
            
    /* you don't need this now,because InteractorType is part of the generic class definition 
    func interactorForType<T>(type: T.Type) -> T {
        return interactor as! T
    }
    */
    
    func routerForType<T>(type: T.Type) -> T {
        return router as! T
    }
}

在此示例中,我指定 IteractorType 符合 InteractorProtocol

在某些情况下,您不需要将周围类型设为泛型,这样您就可以这样做:

open class BasePresenter{
    
    public let router:ILibertyRouter
    public let interactor: InteractorProtocol
    
    public init(router:ILibertyRouter,interactor:InteractorProtocol) {
        self.router = router
        self.interactor = interactor
    }
            
    /* you don't need this now,because InteractorProtocol should define the interface for all Interactors 
    func interactorForType<T>(type: T.Type) -> T {
        return interactor as! T
    }
    */
    
    func routerForType<T>(type: T.Type) -> T {
        return router as! T
    }
}

特别是你可以这样做 if InteractorProtocol 没有,因为可怕的 Swift 编译器错误会说,“自我或相关的类型约束”。基本上,如果您对协议或协议中的关联类型有 where 子句或其他协议一致性约束,那么您只能使用协议来约束通用参数。

如果它没有 Self 或关联的类型约束,那么您可以将其用作普通类型,包括作为存储属性的类型。

代码似乎想要什么

您展示的代码基本上是经典 OOP 风格继承的典型代表。这不是解决问题的唯一方法,但从它的角度来看,它要求定义接口的 Interactor 基类,以及执行实际工作的具体实现。我不知道 Interactor 必须做什么,但如果它足够复杂,您可以应用各种设计模式以避免必须拥有它的无数子类。您可以从 Cocoa/CocoaTouch 中借用声明委托协议的想法,以便您可以通过这种方式修改行为。

无论如何,继承方法相当简单。

open class Iteractor {
    // Just replace the closure parameter types with whatever they're supposed to be
    func findInboxNotifications((Any,ErrorCode) -> Void) { fatalError("Implement in subclass!") }
}

然后将其用作具体交互器类型的基类。

final class AConcreteInteractor: Iteractor {
    override func findInboxNotifications ((Any,ErrorCode) -> Void)  {
        /* Do actual work here */ 
    }
}

然后在您的 BasePresenter 中:

open class BasePresenter {
    
    public let router:ILibertyRouter
    public let interactor: Iteractor
    
    public init(router:ILibertyRouter,interactor: Iteractor) {
        self.router = router
        self.interactor = interactor
    }
            
    /* This isn't needed because you'll use `interactor` polymorphically`
    func interactorForType<T>(type: T.Type) -> T {
        return interactor as! T
    }
    */

    ...
}

InboxListPresenter 然后看起来像这样

public class InboxListPresenter: BasePresenter,ObservableObject {
        
    public init(interactor: InboxListInteractor) {
        
        super.init(router: InboxListRouter(viewData: viewData),interactor: interactor)
    }
    
    func findInboxNotifications() {
        
        iteractor.findInboxNotifications() { inboxNotifications,errorCode in
            
            // do something
        }
    }    
}

使用协议代替继承

我不认为协议很适合这种情况,但在某些方面,协议可以替代基于类的继承;但由于您必须存储交互器,这让您不得不确保没有 Self 或相关的类型约束。

当以这种方式使用时,协议相对于继承的唯一优势是你可以得到一些更好的编译器检查——例如,它可以确保所有需要的实现方法实际上都是由符合协议的类型实现的,而在类继承中,基类总是定义它们(因为与 Java 不同,Swift 不允许您定义纯粹的抽象基类,也不允许您指定像 C++ 这样的“纯”虚拟方法)。您为这种好处付出了代价,因为您可以更严格地定义它们,以便您可以将它们用作存储的属性类型。

我想说的是,您在问题中提供的代码希望为您的交互器使用基于类的继承。除非您有充分的理由不这样做,否则我会选择这样做,而不是试图将方钉塞入圆孔中。