我有一个自动生成的Web服务客户端。我有很多复杂的类,我必须对它进行模式匹配。现在我的结构看起来像这样:
val response = client.getResponse
response match {
case Left(_) => None
case Right(a: SomeClass) => a match {
case SomeClass2(b: Option[SomeClass3]) => b match {
case None => None
case Some(c: SomeClass3) => c match {
case SomeClass4(_, _, _, _, d: Seq[SomeClass4]) => d match {
case Nil => None
case seq: Seq[SomeClass5] => seq match {
case Nil => None
case Seq(xs@_*) => xs map { x =>
x match {
case Nil => None
case SomeClass6(e: SomeClass7) => e match {
case Nil => None
case SomeClass8(f, _, _, _, _) => f match {
case Nil => None
case Seq(xs@_*) => xs map { x =>
x match {
case Nil => None
case SomeClass9(g: Seq[SomeClass9], _, _, _, _, _, _, _, _, _, _) => /* + some nested levels more*/
}
}
}
}
}
}
}
}
}
}
}
其中SomeClass1 - SomeClass9
是case
个类。
正如你所看到的,它似乎令人恐惧。我该怎么办?什么是让它看起来更好的标准方法?
我想不仅应该进行重构,而应该采用另一种方法。
答案 0 :(得分:6)
假设a
应为SomeClass2
,而不是SomeClass
(与b
,c
,d
相同。)
您可以使用case A | B => ...
等替代模式和Some(MyClass(f))
等结构模式。
您也可以在map
中使用部分功能,例如map { case ... }
而不是map { x => x match {...} }
。
我猜您的代码中存在错误:检查case Nil => ...; case SomeClass8(...) => ...
。
您可以将Seq(xs @_*)
替换为xs
。如果您需要整个集合,则无需提取元素。
您的代码:
response match {
case Left(_) | Right(SomeClass2(None)) | Right(SomeClass2(Some(SomeClass3(_, _, _, _, Nil))) => None
case Right(SomeClass2(Some(SomeClass3(_, _, _, _, xs))) =>
xs map {
case SomeClass6(None) | SomeClass6(Some(SomeClass8(Nil, _, _, _, _))) => None
case SomeClass6(Some(SomeClass8(xs, _, _, _, _))) =>
xs map {
case Nil => None
case SomeClass9(g, _, _, _, _, _, _, _, _, _, _) => /* + some nested levels more*/
}
}
}
您还应该将嵌套匹配提取到单独的方法。
模式匹配不是唯一的解决方案。您可以使用Either
和Option
:
response.right.toOption.collect {
// No need for the first part.
case SomeClass2(Some(SomeClass3(_, _, _, _, xs)) if xs.nonEmpty => ...
}
答案 1 :(得分:5)
您可能会发现extractors有用。也可能值得将一些案例弄平,所以你有
case Right(SomeClass(SomeClass2(Some(SomeClass3(value))))) => value ...
case _ => None
而不是在每个级别明确定义None
个案例。
答案 2 :(得分:2)
通过使用理解而不是模式匹配,你可以减少很多这种复杂性。
一个简单的机会是将序列映射到另一个模式匹配:
case seq: Seq[SomeClass5] => seq match {
case Nil => None
case Seq(xs@_*) => xs map { x =>
x match {
...
}
}
}
这非常难看,因为您已使用match
来消除Nil
案例,然后再次匹配seq
。 两个级match
来处理一个对象。这可能会成为
case seq: Seq[SomeClass5] => for (x <- seq) yield {
x match {
...
}
}
这消除了Nil
案例检查并删除了几层嵌套,这是一个巨大的胜利。你至少在两个层面上做到这一点,这是一个更大的胜利。当然,这会返回一个(可能Nil
)序列,而不是f(x)
或None
,但您可以轻松转换它。一种方法是在不添加嵌套的情况下执行此操作:
case seq: Seq[SomeClass5] => (for (x <- seq) yield {
x match {
...
}
}) match {
case Nil => None
case Seq(i) => Some(i)
case ...
}
或者,如果(我怀疑)你希望这些序列只有一个元素......
case seq: Seq[SomeClass5] => (for (x <- seq) yield {
x match {
...
}
}) match {
case Seq(i) => Some(i)
case _ => None
}