我需要实现一个接受多种支付方式的后端系统,然后根据它们的类型进行处理。
我使用了策略模式,但由于类型不匹配,当我初始化服务时,我似乎无法使其工作。 我可能遗漏或误解了 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) ????
}
首先,
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)
处引发异常。
策略模式可能会让这个问题变得过于复杂,但如果不考虑大局,我就无法确定。