Scala中基于列表中固定元素的动态大小写匹配

时间:2019-03-03 12:09:06

标签: scala

假设我有一个可能的列表,信用卡

val availableCreditsCard = List(MasterCardCreditCard, VisaCreditCard)

这两个类都扩展了一个称为CreditCard的特征

现在,我想创建一个名为isValid的方法,该方法接受特征CreditCard

类似的东西:

  def isValid(creditCard: CreditCard): Boolean = {
    creditCard match {
      case t: MasterCardCreditCard => MasterCardCreditCardPayment.isValid(t)
      case s: VisaCreditCard => VisaCreditCardPayment.isValid(s)
    }
  }

MasterCardCreditCardPayment VisaCreditCardPayment 都扩展了一个名为CreditCardPayment的特征。

现在,我的问题是,如果我想添加一个新的可能的CreditCard(Amex)和他自己的CreditCardPayment(AmexCreditCardPayment),但只需更改列表availableCreditsCard并且不触碰< emValid方法中的em> match / case ?

是否可以根据列表availableCreditsCard来动态创建此匹配项/案例?

编辑

这是CreditCardPayment特性。

trait CreditCardPayment[CreditCard] {
  def isValid(creditCard: CreditCard): Boolean
  def checkCVV(creditCard: CreditCard): Boolean
}

以及VisaCreditCardPayment

的示例
object VisaCreditCardPayment extends CreditCardPayment[VisaCreditCard] {

  override def isValid(creditCard: VisaCreditCard): Boolean = {
    val listValidCreditCard = loadFile()
    listValidCreditCard.contains(creditCard)
  }

  //Implemented because it the trait, not necessary
  override def checkCVV(creditCard: VisaCreditCard): Boolean = {
    val listCreditCard = loadFile()
    val cvvToCheck = creditCard.cvv
    listCreditCard.exists(_.cvv == cvvToCheck)
  }
}

现在,您的建议是在isValid方法内使用case t => CreditCardPayment.isValid(t)。由于CreditCardPayment是一个特质:(

2 个答案:

答案 0 :(得分:5)

如果您希望它是“动态的”,则只需使用继承,而不要“在侧面”实现它:

   sealed trait CreditCard {
      def isValid: Boolean
   }

   class MasterCard extends CreditCard {
      def isValid = MasterCardCreditCardPayment.isValid(this)
   }
   class Visa extends CreditCard {
      def isValid = VisaCreditCardPayment.isValid(this)
   }

现在,您只需执行def isValid(c: CreditCard) = c.isValid,无需任何match语句即可保持同步。

添加美国运通卡时:

   class Amex extends CreditCard {
       def isValid = AmexCreditCardPayment.isValid(this)
   }

isValid(amex)可以正常工作,不需要更新任何内容。

答案 1 :(得分:1)

补充Dima的答案。既然如此,您始终希望将isValid逻辑委派给适当的Payment。我将向您展示两种不重新定义每个子类中方法的方法。

一种使用 F界多态性的方法,该方法将需要为每个子类提供相当的样板代码,并且它不是完全 typesafe -因此可能不值得工作。
第二个使用 typeclasses ,这会更 typesafe ,并且每个新卡所需的样板也会更少。
有关 F绑定 Typeclasses 的更详细的讨论,请阅读this文章。

使用F界

sealed trait CreditCardPayment[CC <: CreditCard[CC]] {
  def isValid(creditCard: CC): Boolean
  def checkCVV(creditCard: CC): Boolean
}

object VisaCreditCardPayment extends CreditCardPayment[VisaCreditCard] {
  private final val validCreditCards: List[VisaCreditCard] = ???

  override def isValid(creditCard: VisaCreditCard): Boolean =
    validCreditCards.contains(creditCard)

  override def checkCVV(creditCard: VisaCreditCard): Boolean =
    validCreditCards.exists(_.cvv == creditCard.cvv)
}

object MasterCreditCardPayment extends CreditCardPayment[MasterCreditCard] {
  private final val validCreditCards: List[MasterCreditCard] = ???

  override def isValid(creditCard: MasterCreditCard): Boolean =
    validCreditCards.contains(creditCard)

  override def checkCVV(creditCard: MasterCreditCard): Boolean =
    validCreditCards.exists(_.cvv == creditCard.cvv)
}

sealed trait CreditCard[CC <: CreditCard[CC]] { self: CC =>
  def paymentMethod: CreditCardPayment[CC]

  def cvv: String

  final def isValid: Boolean =
    paymentMethod.isValid(this)
}

final class VisaCreditCard (override val cvv: String) extends CreditCard[VisaCreditCard] {
  override final val paymentMethod: CreditCardPayment[VisaCreditCard] = VisaCreditCardPayment
}

final class MasterCreditCard (override val cvv: String) extends CreditCard[MasterCreditCard] {
  override final val paymentMethod: CreditCardPayment[MasterCreditCard] = MasterCreditCardPayment
}

使用类型类

sealed trait CreditCardPayment[CC <: CreditCard] {
  def isValid(creditCard: CC): Boolean
  def checkCVV(creditCard: CC): Boolean
}

sealed trait CreditCard {
  def cvv: String
}

// Provides the 'isValid' & 'checkCVV' extension methods to any CredictCard.
implicit class CreditCardOps[CC <: CreditCard](val self: CC) extends AnyVal {
  def isValid(implicit payment: CreditCardPayment[CC]): Boolean =
    payment.isValid(self)

  def checkCVV(implicit payment: CreditCardPayment[CC]): Boolean =
    payment.checkCVV(self)
}

final class VisaCreditCard (override val cvv: String) extends CreditCard

object VisaCreditCard {
    final implicit val VisaCreditCardPayment: CreditCardPayment[VisaCreditCard] = new CreditCardPayment[VisaCreditCard] {
      final val validCreditCards: List[VisaCreditCard] = ???

      override def isValid(creditCard: VisaCreditCard): Boolean =
        validCreditCards.contains(creditCard)

      override def checkCVV(creditCard: VisaCreditCard): Boolean =
        validCreditCards.exists(_.cvv == creditCard.cvv)
    }
}

final class MasterCreditCard (override val cvv: String) extends CreditCard

object MasterCreditCard {
  final implicit val MasterCreditCardPayment: CreditCardPayment[MasterCreditCard] = new CreditCardPayment[MasterCreditCard] {
    final val validCreditCards: List[MasterCreditCard] = ???

    override def isValid(creditCard: MasterCreditCard): Boolean =
      validCreditCards.contains(creditCard)

    override def checkCVV(creditCard: MasterCreditCard): Boolean =
      validCreditCards.exists(_.cvv == creditCard.cvv)
  }
}

使用 typeclass 方法,您还可以将isValid的{​​{1}}方法定义为函数。
(通过这种方式,您无需定义和导入CreditCards隐式/值类)

CreditCardOps