Фабричный дизайн шаблона в Scala с кейсом
Я пытаюсь реализовать шаблон проектирования фабрики в Scala, используя методы apply, доступные для объекта-компаньона. У меня есть следующий подход.
sealed trait MyType {
def param: String
}
case class TypeA(param: String) extends MyType
case class TypeB(param: String, anotherParam: String) extends MyType
object MyType {
def apply(param: String): TypeA = ???
def apply(param, anotherParam: String): TypeB = ???
}
Как мне теперь заставить вызывающих из вышеупомянутой черты проходить через объект-компаньон при создании экземпляров TypeA
или же TypeB
?
3 ответа
Вы можете перемещать классы case внутри объекта-компаньона и устанавливать конструкторы как частные и доступ к ним возможен только внутри объекта-компаньона.
sealed trait MyType {
def param: String
}
object MyType {
case class TypeA private[MyType] (param: String) extends MyType
case class TypeB private[MyType] (param: String, anotherParam: String) extends MyType
def apply(param: String): TypeA = TypeA(param)
def apply(param: String, anotherParam: String): TypeB = TypeB(param, anotherParam)
}
Никто не сможет создать экземпляр класса case напрямую, если не будет отражения.
scala> MyType("Test")
res0: MyType.TypeA = TypeA(Test)
scala> MyType("Test", "another test")
res1: MyType.TypeB = TypeB(Test,another test)
scala> MyType.TypeA("test??")
<console>:12: error: constructor TypeA in class TypeA cannot be accessed in object $iw
MyType.TypeA("test??")
^
Вы можете просто позвонить apply
Метод кейсов самих классов. Кажется, нет способа предотвратить вызов клиентского кода TypeA.apply
напрямую, хотя, как это помешало бы MyType
от того, чтобы назвать это.
sealed trait MyType {
def param: String
}
case class TypeA(param: String) extends MyType
case class TypeB(param: String, anotherParam: String) extends MyType
object MyType {
def apply(param: String): TypeA = TypeA(param)
def apply(param: String, anotherParam: String): TypeB = TypeB(param, anotherParam)
}
Черта MyType
запечатан. Что мне другие могут сделать что-то вроде new MyType{}
чтобы создать его экземпляр.
Затем вы можете удалить классы дела.
// No more public case classes TypeA & TypeB
object MyType {
def apply(p: String): MyType = /* case A */ new MyType { val param = p }
private case class InternalB(param: String, other: String) extends MyType
def apply(param: String, anotherParam: String): MyType = InternalB(param, anotherParam)
}
На данный момент, необходимо использовать объект-компаньон для создания MyType
экземпляров.
Затем вы можете восстановить сопоставление с образцом для этих разных случаев.
object MyType {
// the apply functions, plus extractors thereafter...
/** Extracts mandatory parameter whatever is the case. */
def unapply(t: MyType): Option[String] = Some(t.param)
/** Extracts both parameter, extra parameter for case B, None for other */
def unapply(t: MyType): Option[(String, String)] = t match {
case InternalB(mandatory, extra)/* Only possible there as private */ =>
Some(mandatory -> extra)
case _ => None
}
}
// Then pattern matching can do...
val test1: Boolean = MyType("A") match {
case MyType(param) => true
case _ => false
}
// Will be true
val test2: Boolean = MyType("B", "extraB") match {
case MyType(param, extra) => true
case _ => false
}
// Will be true
val test3: Int = MyType("A") match {
case MyType(param, extra) => 2
case MyType(param) => 1
case _ => 0
}
// Will be 1
val test4: Boolean = MyType("B", "extraB") match {
case MyType(param) => true
case _ => false
}
// Will be true
Это позволяет полный контроль над реализацией и абстракцией над реализацией дел.