I want to create object instance whose type is determined by runtime data:
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")
}
}
The above code can do its job but has a problem that it is closed. Whenever I implement a new Business
subclass, I'll have to modify Business.fromData
code, e.g.
case 3 => new Business3
How can I define Business.fromData
once and can later add Business3
, Business4
without registering to it?
Edit
I finally realized that this is a perfect use case of Multimethod
, that is, dispatching based on a function of some argument. So the more general question should be "How to do multimethod in scala"? I believe design patterns exist only because of language incapability, that is why I am reluctant to accept a factory based answer.
This doesn't solve your problem but if you make Business
a "sealed" trait then the compiler will at catch any non-exhaustive match until you've updated fromData
:
sealed trait Business
case class Business1() extends Business
case class Business2() extends Business
biz match {
case Business1 => println("Business1")
}
...will result in...
warning: match is not exhaustive!
missing combination Business2
You could also do this. Although I'm not sure if that is better then match case. Depends on what you are trying to do.
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)
The classic answer is by using a factory with registration, a.k.a. abstract factory.
So given your hierarchy above, you'd create a 'factories' map just like the one presented here in another answer, but you'd also create a parallel hierarchy of object creators, and register them on start up, like so:
trait BusinessCreator {
def createBusiness() : Business
}
object BusinessCreator1() extends BusinessCreator {
override def createBusiness() : Business = new Business1()
factories += "1" -> this
}
//etc.
Another more 'Scalaish' way to do it would be to skip the parallel hierarchy and just register a creator function in the factories object from a companion object to each class, but the idea is the same.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With