将策略模式与泛型一起使用时,Kotlin 中的类型不匹配

问题描述 投票:0回答:1

我需要实现一个接受多种支付方式的后端系统,然后根据它们的类型进行处理。

我使用了策略模式,但由于类型不匹配,当我初始化服务时,我似乎无法使其工作。 我可能遗漏或误解了 kotlin 中泛型的某些内容。

 val gateways =
        listOf(
            CreditGateway(),
            PayPalGateway()
        )
 val api = PaymentAPI(gateways) <-- error: Type mismatch 
 api.authorizeFunds(PayPalModel(...))

 Required:
 List<IPaymentService<IPaymentModel>>
 Found:
 List<IPaymentService<{CreditModel & PayPalModel}>>

代码实现为:

interface IPaymentModel

class CreditModel : IPaymentModel
class PayPalModel : IPaymentModel

interface IPaymentService<in T> where T: IPaymentModel {
    suspend fun authorizeFunds(model: T)
    suspend fun appliesTo(type: IPaymentModel): Boolean
}

class CreditGateway : IPaymentService<CreditModel> {
    override suspend fun authorizeFunds(model: CreditModel) {
      /// implementation
    }
    override suspend fun appliesTo(type: IPaymentModel): Boolean {
        return type is CreditModel
    }
}

class PayPalGateway : IPaymentService<PayPalModel> {
    override suspend fun authorizeFunds(model: PayPalModel) {
      /// implementation
    }
    override suspend fun appliesTo(type: IPaymentModel): Boolean {
        return type is PayPalModel
    }
}

interface IPaymentStrategy<T: IPaymentService<IPaymentModel>> {
    suspend fun <T: IPaymentModel> authorizeFunds(model: T)
}

class PaymentAPI(
    private val paymentServices: List<IPaymentService<IPaymentModel>>
): IPaymentStrategy<IPaymentModel> {

    override suspend fun <T : IPaymentModel> authorizeFunds(model: T) {
        findService(model)?.authorizeFunds(model)
    }
    
    private fun <T : IPaymentModel> findService(model: T) ????
}
java kotlin generics design-patterns strategy-pattern
1个回答
0
投票

首先,

IPaymentStrategy<IPaymentModel>
不是有效类型。
IPaymentStrategy<T>
要求
T
IPaymentService
,而不是
IPaymentModel
。如果你希望它有效,那么
IPaymentStrategy
应该这样声明:

interface IPaymentStrategy<in T: IPaymentModel> {
    // authorizeFunds should not be generic
    suspend fun authorizeFunds(model: T)
}

请注意,如果您要编写的所有策略都是

IPaymentStrategy<IPaymentModel>
,则不需要通用。

其次,您需要找到一种方法来确定某项支付服务是否适用于给定的

IPaymentModel
,以便您可以在
findService
中找到合适的服务。由于类型擦除,您无法直接检查其类型参数。您可以使用
appliesTo
,这需要
findService
来暂停,因为
appliesTo
会暂停。或者,向
IPaymentService
添加一些您可以检查的其他属性。

解决了这个问题,您现在可以写

PaymentAPI
paymentServices
的类型应该是
List<IPaymentService<*>>
,以便您可以拥有异构服务列表。

class PaymentAPI(
    private val paymentServices: List<IPaymentService<*>>
): IPaymentStrategy<IPaymentModel>

现在

PaymentAPI(gateways)
可以编译了。

对于

findService
,你可以这样实现:

private suspend fun findService(model: IPaymentModel): IPaymentService<IPaymentModel>? {
    for (service in paymentServices) {
        if (service.appliesTo(model)) {
            return service as IPaymentService<IPaymentModel>
        }
    }
    return null
}

override suspend fun authorizeFunds(model: IPaymentModel) {
    findService(model)?.authorizeFunds(model)
}

由于类型擦除,未经检查的强制转换

as IPaymentService<IPaymentModel>
是必要的。如果您不小心转换为不采用该类型模型的支付服务,则会在
findService(model)?.authorizeFunds(model)
处引发异常。

策略模式可能会让这个问题变得过于复杂,但如果不考虑大局,我就无法确定。

© www.soinside.com 2019 - 2024. All rights reserved.