Scala 的模式匹配是不是违反了开闭原则?
Posted
技术标签:
【中文标题】Scala 的模式匹配是不是违反了开闭原则?【英文标题】:Does Scala's pattern matching violate the Open/Closed Principle?Scala 的模式匹配是否违反了开闭原则? 【发布时间】:2009-02-18 23:41:21 【问题描述】:如果我添加一个新的案例类,这是否意味着我需要搜索所有模式匹配代码并找出需要处理新类的位置?我最近一直在学习这门语言,当我读到一些支持和反对模式匹配的论点时,我一直对应该在哪里使用它感到困惑。请参阅以下内容:
专业版: Odersky1 和 Odersky2
缺点: Beust
cmets 在每种情况下也都非常好。那么模式匹配是令人兴奋的东西还是我应该避免使用的东西?实际上,我想答案是“这取决于您何时使用它”,但它有哪些积极的用例,哪些是消极的用例?
【问题讨论】:
Beust 链接已损坏,显然是更改了 URL 方案。我试图看看我是否能弄清楚是哪个帖子,但我不确定。请提供更正的链接,或提供更多信息(帖子的标题是什么?)。 相关:How does Pattern Matching in Scala overcome duplication that switch case causes? 【参考方案1】:杰夫,我认为你的直觉是正确的:这取决于。
当您有一组相对固定的方法需要实现时,具有虚拟方法分派的面向对象的类层次结构很好,但许多潜在的子类可能从层次结构的根部继承并实现这些方法。在这样的设置中,添加新子类相对容易(只需实现所有方法),但添加新方法相对困难(您必须修改所有子类以确保它们正确实现新方法)。
当您拥有一组相对固定的属于某个数据类型的类时,具有基于模式匹配功能的数据类型是很好的,但有许多潜在的函数对该数据类型进行操作。在这样的设置中,为数据类型添加新功能相对容易(只需对其所有类进行模式匹配),但添加作为数据类型一部分的新类相对困难(您必须修改所有匹配的函数以确保它们正确支持新类)。
面向对象方法的典型示例是 GUI 编程。 GUI 元素需要支持非常少的功能(在屏幕上绘制自己是最低限度的),但新的 GUI 元素一直在添加(按钮、表格、图表、滑块等)。模式匹配方法的典型示例是编译器。编程语言通常具有相对固定的语法,因此语法树的元素很少更改(如果有的话),但不断添加对语法树的新操作(更快的优化、更彻底的类型分析等)。
幸运的是,Scala 允许您将这两种方法结合起来。案例类既可以进行模式匹配,也可以支持虚拟方法分派。常规类支持虚拟方法分派,并且可以通过在相应的伴随对象中定义提取器来进行模式匹配。每种方法何时适合由程序员决定,但我认为两者都有用。
【讨论】:
【参考方案2】:虽然我尊重 Cedric,但他在这个问题上完全错了。 Scala 的模式匹配可以在需要时从类更改中完全封装。虽然对 case 类 的更改确实需要更改任何相应的模式匹配实例,但这仅适用于以幼稚的方式使用此类类时。
Scala 的模式匹配 always 委托给类的伴随对象的解构器。使用案例类,这个解构器是自动生成的(连同伴随对象中的工厂方法),尽管仍然可以覆盖这个自动生成的版本。在任何时候,您都可以完全控制模式匹配过程,从而将任何模式与类本身的潜在变化隔离开来。因此,模式匹配只是通过封装的安全过滤器访问类数据的另一种方式,就像任何其他方法一样。
因此,Odersky 博士的意见在这里值得信赖,特别是考虑到他在面向对象编程和设计领域进行的大量研究。
至于应该用在什么地方,完全看个人口味。如果它使您的代码更简洁和可维护,请使用它!否则,不要。对于大多数面向对象的程序,模式匹配是不必要的。然而,一旦你开始集成更多的函数式习语(Option
、List
等),我想你会发现模式匹配将显着减少语法开销并提高类型系统提供的安全性。一般来说,只要您想在测试某些条件的同时提取数据(例如,从 Some
中提取值),就可能会用到模式匹配。
【讨论】:
【参考方案3】:如果你在做函数式编程,模式匹配绝对是好的。在 OO 的情况下,在某些情况下它是好的。在 Cedric 的示例本身中,这取决于您如何从概念上查看 print()
方法。这是每个Term
对象的行为吗?还是它之外的东西?我会说它在外面,并且进行模式匹配是有意义的。另一方面,如果您有一个带有各种子类的 Employee
类,那么在基类中对它的属性(比如名称)进行模式匹配是一个糟糕的设计选择。
模式匹配还提供了一种解包类成员的优雅方式。
【讨论】:
以上是关于Scala 的模式匹配是不是违反了开闭原则?的主要内容,如果未能解决你的问题,请参考以下文章