我非常喜欢Scala的抽象工厂模式,但是我很难让它适用于我的用例。我有一些可选参数,我想用它来创建一个通用类,可以通过我的工厂抽出来识别具体类型。以下是我正在使用的特征和模型类型的示例:
trait Animal {
def id: Long
}
trait Pet {
def name: String
}
trait Pest {
def hasDisease: Boolean
}
然后一个示例案例类将是:
case class Dog(id: Long, name: String) extends Animal with Pet
case class Cat(id: Long, name: String) extends Animal with Pet
case class Rat(id: Long, hasDisease: Boolean) extends Animal with Pest
case class Cow(id: Long) extends Animal
然后工厂看起来像这样:
object Animal {
def apply(id: Long, name: String) = name match {
case "Lassie" => Dog(id, name)
case "Garfield" => Cat(id, name)
}
def apply(id: Long, hasDisease: Boolean) = Rat(id, hasDisease)
def apply(id: Long) = Cow(id)
所以在REPL中这很有用,我可以这样做:
Animal(id=2, name="Lassie")
res5: Dog = Dog(2,"Lassie")
Animal(id=1)
res6: Cow = Cow(1)
但是在我的资源中,因为参数是可选的(name,hasDisease),我需要能够像这样构造我的抽象动物对象:
Animal(id=1, name=None, hasDisease=None)
res7: Cow = Cow(1)
有关如何使这项工作的想法吗?
修改
我不一定致力于这种模式,但这只是我的第一次尝试。总体目标是我有'n'个可选查询参数,并根据哪些存在,我想映射到具体的表示
编辑2 正如SergGr指出的那样,一种可能性就是存在参数的情况匹配
object Animal {
def apply(id: Long, nameOpt: Option[String] = None, hasDiseaseOpt: Option[Boolean] = None) = (nameOpt, hasDiseaseOpt) match {
case (Some(_), Some(_)) => throw new IllegalArgumentException("Animal can't have both name and disease")
case (None, Some(hasDisease)) => Rat(id, hasDisease)
// different approaches to match values
case (Some("Lassie"), None) => Dog(id, "Lassie")
case (Some(name), None) if "Garfield".equals(name) => Cat(id, name)
case (Some(name), None) => throw new IllegalArgumentException(s"Unknown car or dog name '$name'")
case (None, None) => Cow(id)
}
}
如果我们只有两个其他可选参数,这会很有效,但是我们可以添加另一个可选参数,这会使处理这个逻辑变得有点棘手
答案 0 :(得分:3)
我仍然不确定我是否理解你的问题。首先,在Scala中表示可选项的典型方法是scala.util.Option
。
我不知道一种简单的方法可以使这样的代码编译时安全且仍然可用。由于运行时错误取决于您的实际目标,我看到两种方法:
object Animal {
def apply(id: Long, nameOpt: Option[String] = None, hasDiseaseOpt: Option[Boolean] = None) = (nameOpt, hasDiseaseOpt) match {
case (Some(_), Some(_)) => throw new IllegalArgumentException("Animal can't have both name and disease")
case (None, Some(hasDisease)) => Rat(id, hasDisease)
// different approaches to match values
case (Some("Lassie"), None) => Dog(id, "Lassie")
case (Some(name), None) if "Garfield".equals(name) => Cat(id, name)
case (Some(name), None) => throw new IllegalArgumentException(s"Unknown car or dog name '$name'")
case (None, None) => Cow(id)
}
}
id
匹配输出类型
醇>
object Animal {
def apply(id: Long, nameOpt: Option[String] = None, hasDiseaseOpt: Option[Boolean] = None) = (id, nameOpt, hasDiseaseOpt) match {
case (1, None, None) => Cow(id)
case (2, Some(name), None) => Dog(id, name)
case (3, Some(name), None) => Cat(id, name)
case (4, None, Some(hasDisease)) => Rat(id, hasDisease)
case (id, _, _) if id >= 5 => throw new IllegalArgumentException(s"Unknown id = $id")
case _ => throw new IllegalArgumentException(s"Unepxected combination of parameters: id = $id, name = $nameOpt, hasDisease = $hasDiseaseOpt")
}
}
在这两种情况下,您都可以添加辅助非可选方法,例如
// note that String might be null so it makes sense to use Option(name) rather than Some(name)
def apply(id: Long, name: String) = apply(id, nameOpt = Option(name))
def apply(id: Long, hasDisease: Boolean) = apply(id, hasDiseaseOpt = Some(hasDisease))
P.S。我怀疑这两种解决方案可能都不是你真正想要的。在这种情况下,你应该更好地描述你的真实问题。