抽象类,带有Scala中具体定义的可选参数
我真的很喜欢Scala的抽象工厂模式,但我很难让它适用于我的用例。我有“n”个可选参数,我想用来创建一个泛型类,它可以通过我的工厂来识别具体类型。下面是我正在使用的特征和模型类型的一个示例:抽象类,带有Scala中具体定义的可选参数,scala,abstract-class,factory,Scala,Abstract Class,Factory,我真的很喜欢Scala的抽象工厂模式,但我很难让它适用于我的用例。我有“n”个可选参数,我想用来创建一个泛型类,它可以通过我的工厂来识别具体类型。下面是我正在使用的特征和模型类型的一个示例: trait Animal { def id: Long } trait Pet { def name: String } trait Pest { def hasDisease: Boolean } 然后一个示例案例类是: case class Dog(id: Long, n
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)
Animal(id=1, name=None, hasDisease=None)
res7: Cow = Cow(1)
因此,在REPL中,这非常有效,我可以:
Animal(id=2, name="Lassie")
res5: Dog = Dog(2,"Lassie")
Animal(id=1)
res6: Cow = Cow(1)
但是在我的资源中,由于参数是可选的(名称,hasDisease),我需要能够像这样构造抽象的动物对象:
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)
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)
}
}
如果我们只有另外两个可选参数,这将非常有效,但我们可能会添加另一个可选参数,这将使处理此逻辑变得有点棘手我仍然不确定我是否正确理解了您的问题。首先,在Scala中表示可选内容的典型方法是 我不知道有什么简单的方法可以使这样的代码在编译时安全并且仍然可用。由于运行时错误取决于您的实际目标,我看到两种方法:
目标动物{
def apply(id:Long,nameOpt:Option[String]=None,hasdiseopt:Option[Boolean]=None)=(nameOpt,hasdiseopt)匹配{
case(Some(),Some())=>抛出新的IllegalArgumentException(“动物不能同时有名称和疾病”)
病例(无,部分(hasDisease))=>大鼠(id,hasDisease)
//匹配值的不同方法
案例(部分(“莱西”),无)=>狗(id,“莱西”)
如果“Garfield”,则为case(部分(名称),无)。等于(名称)=>Cat(id,名称)
case(Some(name),None)=>抛出新的IllegalArgumentException(s“未知汽车或狗名'$name'))
案例(无,无)=>Cow(id)
}
}
id
匹配输出类型目标动物{
def apply(id:Long,nameOpt:Option[String]=None,hasdiseopt:Option[Boolean]=None)=(id,nameOpt,hasdiseopt)匹配{
案例(1,无,无)=>Cow(id)
案例(2,部分(名称),无)=>狗(id,名称)
案例(3,部分(名称),无)=>类别(id,名称)
病例(4,无,部分(hasDisease))=>大鼠(id,hasDisease)
如果id>=5=>则抛出新的IllegalArgumentException(s“未知id=$id”),则为case(id,,,x)
案例=>抛出新的IllegalArgumentException(s“未选中的参数组合: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))
另外,我怀疑这两种解决方案可能都不是你真正想要的。在这种情况下,你应该更好地描述你真正的问题。如果有人通过了一个不可能的参数组合,比如动物(id=2,name=“Lassie”,hasDisease=true),那么预期的结果是什么?理想情况下,我希望捕捉并抛出一个类似于他们试图在不通过nameprolux的情况下获取狗对象的错误,我不确定你最后的评论:如果没有名字,你怎么知道
狗
是预期的类型而不是牛
?对不起,我试图抽象我的实际代码,但这个用例非常愚蠢。在我的实际代码中,我打开了id。因此,如果您尝试只传递id=2,但2需要字符串param name,那么对于糟糕的解释,这将是一个非常糟糕的错误。如果我们仅限于另外两个可选参数,那么您建议的第一种方法将非常好。有可能增加更多,这将使管理这种逻辑有点痛苦。方法2实际上是一种可能性,因为我们有100多个已定义的ID,但只有少数几个具有特定参数。helper非可选方法非常有用。非常感谢。