2015-05-28 3 views
1

Я пытаюсь построить завод для реализации общей черты.Как создать фабрику для параметризованного класса?

Учитывая мою модель домена:

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]

Я не могу найти подходящее решение, у любого из вас, у вас есть несколько советов для меня?

+0

Обычно полезно сказать, что сообщение об ошибке вместо «не будет компилироваться». –

ответ

1

Как насчет этого?

  • Вместо того, чтобы использовать строку, использовать герметичный признак, который знает, как создать соответствующий репозиторий
  • Используйте 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 напрямую :)

+0

Мне нравится этот подход ... Я попробовал что-то, используя объекты case, чтобы решить проблему, но я не мог понять это. Спасибо за решение! –

0

Возможно, вам стоит подумать о том, чего вы пытаетесь достичь. В вашем примере, какой тип вы ожидали бы вернуть RepositoryFactory.create[Man]("woman")? Компилятор не имеет возможности связать эти произвольные строки с типами.

Смежные вопросы