【问题标题】:Dinamic case match in Scala based on fixed elements in a listScala中基于列表中的固定元素的动态大小写匹配
【发布时间】:2019-03-03 12:09:05
【问题描述】:

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

val availableCreditsCard = List(MasterCardCreditCard, VisaCreditCard)

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

现在,我想创建一个名为 isValid 的方法,它接受 trait CreditCard

类似:

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

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

所以,现在,我的问题是,如果我想用他自己的 CreditCardPayment (AmexCreditCardPayment) 添加一个新的可能的 CreditCard (Amex),但只需更改列表 availableCreditsCard 并且不触及 在 isValid 方法中匹配/大小写

是否可以根据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 是一个特征:(

【问题讨论】:

  • 如果您要更改要求,我认为您也必须更改代码,并且应该避免任何错误。
  • 是的,我完全同意这一点,我自己想,好吧,是的,如果我添加一种新的付款方式,我应该更改代码本身以更加确定一切都会正常工作,但是,您认为这有可能吗?
  • 只需在CreditCard 上创建一个方法.isValid。然后你可以只做creditCard.isValid() 没有任何辅助代码来保持同步。
  • 如果你想在所有其他类型的卡片上执行一些通用功能,你可以在你的火柴盒中写上_case。
  • @AntonioCalì 你没有在特征上实现它,只是在那里声明它。然后为每个子类实现。

标签: scala


【解决方案1】:

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

   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) 可以正常工作,无需更新。

【讨论】:

  • 这几乎是一种“访客”设计模式,对吧?好主意,顺便说一句
  • @Tizianoreica 我不知道那个“访问者”是什么东西 :) 回到我的时代,这只是被称为封装 :D
【解决方案2】:

补充 Dima 的回答。因为,您总是希望将isValid 逻辑委托给适当的Payment。我将向您介绍两种不重新定义每个子类中的方法的方法。

一种使用 F-Bounded Polymorphism 的方法,每个子类都需要相当多的样板代码,而且它不是完全类型安全 - 因此可能不值得工作。
第二个使用 typeclasses,这将更加typesafe,并且每张新卡所需的样板文件更少。
有关 F-BoundedTypeclasses 的更详细讨论,请阅读 this 文章。

使用 F-Bounded

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 方法,您还可以将 CreditCardsisValid 方法定义为一个函数。
(这样您就不需要定义和导入CreditCardOps隐式/值类).

def isValid[CC <: CreditCard](cc: CC)(implicit payment: CreditCardPayment[CC]): Boolean =
  payment.isValid(cc)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-07-03
    • 2015-05-30
    • 2018-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-09
    • 1970-01-01
    相关资源
    最近更新 更多