scala动态确定类型

时间:2012-12-02 04:40:45

标签: scala

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

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 个答案:

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