我是否正确使用(Scala)泛型?

时间:2018-10-19 11:06:55

标签: scala generics nested-generics

我目前的代码结构如下:

object Example {
  val doctor : Healer[Cancer]   = new Doctor
  val parent : Healer[Cold]     = new Parent
  val generic: Healer[Sickness] = new Generic

  def cure(sickness: Sickness): Boolean = sickness match {
    case cancer: Cancer => doctor.cure(cancer)
    case cold  : Cold   => parent.cure(cold)
    case other          => generic.cure(other)
  }
}

class Sickness
class Cancer extends Sickness
class Cold   extends Sickness
// other sicknesses

abstract class Healer[A <: Sickness] {
  def cure(sickness: A): Boolean
}

abstract class Treatment[A <: Sickness] {
  def cure(sickness: A): Boolean
}

class Doctor[A <: Cancer] extends Healer[A] {
  val treatments: List[Treatments[A]] = List(
    new Chemotherapy,
    new Surgery,
    new HopesAndPrayers
  )

  def cure(sickness: A): Boolean = {
    // ... choose a treatment
    treatment.cure(sickness)
  }
}

class Chemotherapy[A <: Cancer] extends Treatment[A] {
  def cure(cancer: A): Boolean = {
    // without generics, needs a check for Cancer
  }
}

// other Healers (e.g. Parent, Generic) and other Treatments (e.g. BedRest, HealthyFood)

这是正确/正常使用泛型的方式吗?我应该/应该将其隔离到Treatment和/或Healer吗?如果是,怎么办?

为进一步解释,这是在没有泛型的情况下开始的,但是当我开始创建Treatments的子类(例如Chemotherapy)时,我最终使用了包含{{1}的cure(sickness: Sickness)方法}。看来,如果我有一个仅处理sickness match { case cancer: Cancer => ... }的类SomeSpecificTreatment,那么使用方法SomeSpecificSickness而不是cure(sickness: SomeSpecificSickness)似乎是有意义的。

因此,我在cure(sickness: Sickness)中添加了泛型,因此,它们又发展到Treatment。发生这种情况时,我通常会觉得自己做错了。我对Example特别怀疑。随着Example#cure的更多子类,案例列表可能会变得很长,我本能地认为可以以不同的方式来做,更好(可以吗?)。我曾考虑过使用Sickness做类似Map的事情,但是它的行为与模式匹配类型不同,而且无论如何我通常都很难嵌套通用。

因为这可能出现:我无法控制healers.getOrElse(sickness.getClass, generic)或其子类,也无法更改Sickness的签名

1 个答案:

答案 0 :(得分:2)

我通常发现,迟早设计相关的类层次结构时,会出现破坏模型并最终被黑客入侵的事情。

您是否考虑过使用类型类,它是功能编程语言中非常常见的模式。

case class Cancer(name: String)
case class Cold(name: String)

sealed trait Healer[S] {
  def name: String
  def cure(sickness: S): Unit = println(s"$name curing $sickness")
}

implicit val oncologist = new Healer[Cancer] {
  val name = "Oncologist"
}

implicit val mum = new Healer[Cold] {
  val name = "Mum"
}

def cure[S : Healer](sickness: S) = { 
  val healer = implicitly[Healer[S]]  
  healer.cure(sickness)
}

scala> cure(Cancer("Lung cancer"))
Oncologist curing Cancer(Lung cancer)

scala> cure(Cold("Flu"))
Mum curing Cold(Flu)

否则,不相关的类型可以是Typeclass的成员,该类定义某种常见行为,例如排序。

例如,我们可能希望医务人员处理可能不适合一个很好的班级等级的不相关疾病,例如:Cancer,Burns和BrokenLeg。另一个优点是我们可以限制Typeclass的范围。在我们要治愈病人的医院里,治疗师可能会很有意义。在一家保险公司中,我们可能会对其他事情感兴趣,例如某种疾病的治疗费用。