7

我想创建其类型由运行时数据确定的对象实例:

trait Business
case class Business1() extends Business 
case class Business2() extends Business

object Business {
  def fromData(data:Array[Byte]): Business = data(0) match {
    case 1 => new Business1
    case 2 => new Business2
    case _ => throw new RuntimeException("data error")
  }
}

上面的代码可以完成它的工作,但有一个问题是它被关闭了。每当我实现一个新的Business子类时,我都必须修改Business.fromData代码,例如

case 3 => new Business3

我如何定义Business.fromData一次,以后可以添加Business3Business4而不注册它?

编辑

我终于意识到这是一个完美的用例Multimethod,即基于某个参数的函数进行调度。所以更普遍的问题应该是“如何在scala中做多方法”?我相信设计模式的存在只是因为语言无能,这就是为什么我不愿意接受基于工厂的答案。

4

4 回答 4

3

你也可以这样做。虽然我不确定这是否比匹配案例更好。取决于你想要做什么。

class Business {
  override def toString = "Business"
}

val factories: Map[Int, () => Business] = Map(
  1 -> (() => new Business {
    override def toString = "Business1"
  }),
  2 -> (() => new Business {
    override def toString = "Business2"
  }),
  3 -> (() => new Business {
    override def toString = "Business3"
  })
)

object Business {
  def fromData(data: Array[Byte]): Business = factories(data(0))()
}

val b = Business.fromData(Array(1,1,2))
println(b)
于 2012-12-02T10:33:16.410 回答
3

这并不能解决您的问题,但是如果您制作Business了“密封”特征,那么编译器将捕获任何非详尽匹配,直到您更新fromData

sealed trait Business
case class Business1() extends Business
case class Business2() extends Business

biz match {
    case Business1 => println("Business1")
}

……会导致……

warning: match is not exhaustive!
missing combination            Business2
于 2012-12-02T06:22:33.377 回答
1

经典的答案是使用带有注册的工厂,也就是抽象工厂。

因此,鉴于您上面的层次结构,您将创建一个“工厂”地图,就像在另一个答案中提供的那样,但您还将创建对象创建者的并行层次结构,并在启动时注册它们,如下所示:

trait BusinessCreator {
  def createBusiness() : Business 
}
object BusinessCreator1() extends BusinessCreator {
  override def createBusiness() : Business = new Business1()

  factories += "1" -> this
}
//etc.

另一种更“Scalaish”的方法是跳过并行层次结构,只需在工厂对象中从伴随对象注册到每个类的创建者函数,但想法是相同的。

于 2012-12-02T11:44:39.607 回答
0

这个如何?

val factories = collection.mutable.Map(
  1 -> new Function0[Business] { 
    private[this] lazy val single = new Business {
      override def toString = "Business1"
    }
    override def apply() = single 
  }
  ,2 -> new Function0[Business] { 
    private[this] lazy val single = new Business {
      override def toString = "Business2"
    }
    override def apply() = single 
  }
  ,3 -> new Function0[Business] { 
    private[this] lazy val single = new Business {
      override def toString = "Business3"
    }
    override def apply() = single 
  }
)
于 2012-12-02T23:50:18.203 回答