9

我有一个自动生成的 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上课。

如您所见,这似乎很可怕。我该怎么办?让它看起来更好的标准方法是什么?

我想不仅应该有重构,还应该有另一种方法。

4

3 回答 3

8

假设a应该是SomeClass2,但不是SomeClass(与b, c,相同d)。

您可以使用替代模式case A | B => ...和结构模式,如Some(MyClass(f)).

map您也可以在like中使用部分函数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*/
        }
    }
}

您还应该将嵌套匹配项提取到单独的方法中。

模式匹配不是唯一的解决方案。您可以使用Eitherand的方法Option

response.right.toOption.collect {
  // No need for the first part.
  case SomeClass2(Some(SomeClass3(_, _, _, _, xs)) if xs.nonEmpty => ...
}
于 2013-09-17T04:51:37.010 回答
5

您可能会发现提取器很有用。也可能值得将一些案例展平,这样你就有了

case Right(SomeClass(SomeClass2(Some(SomeClass3(value))))) => value ...
case _ => None

而不是None在每个级别明确定义一个案例。

于 2013-09-17T04:37:34.263 回答
2

您可以通过使用 for 理解而不是模式匹配来减少很多这种复杂性。

一个简单的机会是将序列映射到另一个模式匹配:

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)or 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
}
于 2013-09-17T10:41:50.307 回答