如何为参数化类创建工厂?

时间:2015-05-28 13:57:56

标签: scala generics type-systems

我正在尝试为通用特征的实现构建工厂。

鉴于我的域名模式:

trait Person

case class Man(firstName: String, lastName: String) extends Person  
case class Woman(firstName: String, lastName: String) extends Person

我为这些类创建了一个存储库:

trait Repository[T <: Person] {
  def findAll(): List[T]
}

class ManRepository extends Repository[Man] {
  override def findAll(): List[Man] = {
    List(
      Man("Oliver", "Smith"),
      Man("Jack", "Russel")
    )
  }
}

class WomanRepository extends Repository[Woman] {
  override def findAll(): List[Woman] = {
    List(
      Woman("Scarlet", "Johnson"),
      Woman("Olivia", "Calme")
    )
   }
 }

到目前为止,这么好,一些非常简单的课程。但是我想根据一些参数创建一个工厂来创建这些存储库的实例。

object RepositoryFactory {
  def create[T <: Person](gender: String): Repository[T] = {
    gender match {
      case "man" => new ManRepository()
      case "woman" => new WomanRepository()
    }
  }
}

但这最后一部分不会编译。如果我省略工厂的显式返回类型,它会编译但返回类型为Repository[_1]的存储库而不是Repository[Man]

我似乎无法找到合适的解决方案,你们中的任何人都有一些技巧吗?

2 个答案:

答案 0 :(得分:1)

这个怎么样?

  • 使用知道如何创建适当的repo的密封特性
  • ,而不是使用字符串
  • 使用dependent types返回正确类型的存储库

代码:

object GenderStuff {

  trait Person
  case class Man(firstName: String, lastName: String) extends Person
  case class Woman(firstName: String, lastName: String) extends Person

  // Note that Repository has to be covariant in P.
  // See http://blogs.atlassian.com/2013/01/covariance-and-contravariance-in-scala/ for explanation of covariance.
  trait Repository[+P <: Person] {
    def findAll(): List[P]
  }

  class ManRepository extends Repository[Man] {
    override def findAll(): List[Man] = {
      List(
        Man("Oliver", "Smith"),
        Man("Jack", "Russel")
      )
    }
  }

  class WomanRepository extends Repository[Woman] {
    override def findAll(): List[Woman] = {
      List(
        Woman("Scarlet", "Johnson"),
        Woman("Olivia", "Calme")
      )
    }
  }

  sealed trait Gender {
    type P <: Person
    def repo: Repository[P]
  }
  case object Male extends Gender {
    type P = Man
    def repo = new ManRepository()
  }
  case object Female extends Gender {
    type P = Woman
    def repo = new WomanRepository()
  }

  object RepositoryFactory {
    def create(gender: Gender): Repository[gender.P] = gender.repo

    // or if you prefer you can write it like this 
    //def create[G <: Gender](gender: G): Repository[G#P] = gender.repo
  }

  val manRepo: Repository[Man] = RepositoryFactory.create(Male)
  val womanRepo: Repository[Woman] = RepositoryFactory.create(Female)
}

当然,这使得工厂对象毫无意义 - 直接调用Male.repo更容易:)

答案 1 :(得分:0)

你应该考虑一下你正在努力实现的目标。在您的示例中,您希望RepositoryFactory.create[Man]("woman")返回哪种类型?编译器无法将这些任意字符串与类型相关联。

相关问题