我想创建其类型由运行时数据确定的对象实例:
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
一次,以后可以在不注册的情况下添加Business3
,Business4
?
修改
我终于意识到这是Multimethod
的完美用例,即基于某个参数的函数进行调度。所以更一般的问题应该是“如何在scala中进行多方法”?我认为设计模式只是因为语言无能力而存在,这就是为什么我不愿意接受基于工厂的答案。
答案 0 :(得分: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
答案 1 :(得分: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)
答案 2 :(得分:1)
经典的答案是使用一家名为a.k.a. abstract factory的工厂。
因此,考虑到上面的层次结构,你可以创建一个“工厂”地图,就像在另一个答案中提供的那样,但你也要创建一个对象创建者的并行层次结构,并在启动时注册它们,就像这样:
trait BusinessCreator {
def createBusiness() : Business
}
object BusinessCreator1() extends BusinessCreator {
override def createBusiness() : Business = new Business1()
factories += "1" -> this
}
//etc.
另一种“Scalaish”方法是跳过并行层次结构,只是在工厂对象中将一个创建者函数从一个伴随对象注册到每个类,但这个想法是一样的。
答案 3 :(得分: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
}
)