3

我正在尝试使用伴随对象上可用的应用方法在 Scala 中实现工厂设计模式。我有以下方法。

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在创建or的实例时,我现在如何强制上述特征的调用者通过伴随对象TypeB

4

3 回答 3

12

您可以在伴生对象内移动案例类,并将构造函数设置为私有且仅在伴生对象内访问。

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

除非通过反射,否则没有人能够直接实例化案例类。

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??")
                     ^
于 2014-09-05T17:08:38.687 回答
1

您可以简单地调用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)
}
于 2014-09-05T13:50:18.117 回答
1

特征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

它允许对实例化进行完全控制,并对案例的实现进行抽象。

于 2014-09-05T13:51:05.340 回答