[英]Scala Factory Pattern with Generic
I want to use Factory Method with Generics which can work with specific implementations.我想将工厂方法与泛型一起使用,它可以与特定的实现一起使用。 In service classes i want to have type safety but in controller to operate only know interface.在服务类中,我想要类型安全,但在控制器中操作只知道接口。
I have defined different types of operation type我定义了不同类型的操作类型
trait Transaction {
val amount: BigDecimal
}
case class CreditCardTransaction(amount: BigDecimal, ccNumber: String, expiry: String) extends Transaction
case class BankTransaction(amount: BigDecimal, bankAccount: String) extends Transaction
and Services which can work with specific operation types和可以与特定操作类型一起使用的服务
trait Service[T <: Transaction] {
def transfer(transaction: T)
}
class CCService() extends Service[CreditCardTransaction] {
override def transfer(transaction: CreditCardTransaction): Unit = println("pay with cc")
}
class TTService() extends Service[BankTransaction] {
override def transfer(transaction: BankTransaction): Unit = println("pay with telex transfer")
}
I have created factory with concrete instances我用具体实例创建了工厂
class PaymentSystemFactory(ccService: CCService, ttService: TTService) {
def getService(paymentMethod: String) = paymentMethod match {
case "cc" => ccService
case "tt" => ttService
}
}
And parser to get specific transaction from external service和解析器从外部服务获取特定事务
object Parser {
def parse(service: Service[_ <: Transaction]) = service match {
case _: Service[CreditCardTransaction] => CreditCardTransaction(100, "Name", "01/01")
case _: Service[BankTransaction] => BankTransaction(100, "1234")
}
}
But that code doesn't want to compile due provided types mismatch from PaymentSystemFactory method:但是该代码不想从 PaymentSystemFactory 方法中编译由于提供的类型不匹配:
object App {
val factory = new PaymentSystemFactory(new CCService, new TTService)
val service = factory.getService("cc") // return Service[_ >: CreditCardTransaction with BankTransaction <: Transaction]
val transaction: Transaction = Parser.parse(service)
service.transfer(transaction) // Failed here: Required _$1 found Transaction
}
I would be happy to avoid type erasure if possible due factory method call and wondered why that code doesn't work如果可能,由于工厂方法调用,我很乐意避免类型擦除,并想知道为什么该代码不起作用
What you've done here (apparently accidentally?) is to create a generalized algebraic data type, or GADT for short.您在这里所做的(显然是意外?)是创建一个广义代数数据类型,或简称为 GADT。 If you want to find out more about this feature, that is probably a useful term to search for.如果您想了解有关此功能的更多信息,这可能是一个有用的搜索词。
As for how to make this work: The type signature of the parse
method needs to reflect that the type of the returned transaction matches service's transaction type.至于如何实现: parse
方法的类型签名需要反映返回事务的类型与服务的事务类型匹配。
Also, you can't do case _: Service[CreditCardTransaction]
, that won't work properly due to erasure.此外,您不能执行case _: Service[CreditCardTransaction]
,因为擦除而无法正常工作。 Use case _: CCService
instead.用case _: CCService
代替。
Try this:尝试这个:
object Parser {
def parse[A <: Transaction](service: Service[A]): A = service match {
case _: CCService => CreditCardTransaction(100, "Name", "01/01")
case _: TTService => BankTransaction(100, "1234")
}
}
And you'll need to change the calling code too:您还需要更改调用代码:
object App {
val factory = new PaymentSystemFactory(new CCService, new TTService)
factory.getService("cc") match {
case service: Service[a] =>
val transaction: a = Parser.parse(service)
service.transfer(transaction)
}
}
Note that the match
isn't used to actually distinguish between multiple cases.请注意, match
并不用于实际区分多种情况。 Instead, its only purpose here is to give a name to the transaction type, a
in this case.取而代之的是,在这里它的唯一目的是给一个名称交易类型, a
在这种情况下。 This is one of the most obscure features in the Scala language.这是 Scala 语言中最晦涩的特性之一。 When you do a pattern match on a wildcard type and use a lower-case name like a
for the type parameter, then it doesn't check that the type is a
(like it would for an uppercase name), but it creates a new type variable that you can use later on.当您对通配符类型进行模式匹配并为类型参数使用小写名称(如a
,它不会检查该类型是否为a
(就像检查大写名称一样),但它会创建一个新的类型变量,您可以稍后使用。 In this case, it is used to declare the transaction
variable, and also implicitly to call the Parser.parse
method.在这种情况下,它用于声明transaction
变量,也隐式调用Parser.parse
方法。
While I was making the below solution, @Mathias proposed another one that I find really nice.在我制定以下解决方案时,@Mathias 提出了另一个我觉得非常好的解决方案。 But I still post it as an alternative that might be interesting.但我仍然将其发布为可能有趣的替代方案。
Instead of type argument (ie generic), you can use type member:您可以使用类型成员,而不是类型参数(即泛型):
trait Service {
type T <: Transaction
def transfer(transaction: T): Unit
}
class CCService() extends Service {
type T = CreditCardTransaction
override def transfer(transaction: CreditCardTransaction): Unit = println("pay with cc")
}
class TTService() extends Service {
type T = BankTransaction
override def transfer(transaction: BankTransaction): Unit = println("pay with telex transfer")
}
// need to use asInstanceOf, I don't know how to tell scala that the type is safe
def parse(service: Service): service.T = service match {
case _: CCService => CreditCardTransaction(100, "Name", "01/01").asInstanceOf[service.T]
case _: TTService => BankTransaction(100, "1234").asInstanceOf[service.T]
}
val factory = new PaymentSystemFactory(new CCService, new TTService)
val service = factory.getService("tt")
val transaction = parse(service) // transaction has type service.T
service.transfer(transaction)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.