Search code examples
scalacompanion-object

Factory design pattern in Scala with case classes


I'm trying to implement a factory design pattern in Scala using the apply methods available on the companion object. I have the following approach.

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 = ???
}

How do I now force the callers of the above trait to go via the companion object when creating instances of TypeA or TypeB?


Solution

  • You can move the case classes inside the companion object, and set the constructors to be private and accessed only within the companion object.

    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)
    }
    

    No one would be able to instantiate the case classes directly, unless though reflection.

    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??")
                         ^