gpt4 book ai didi

design-patterns - 有区别的工会是否与开放关闭原则相冲突

转载 作者:行者123 更新时间:2023-12-04 00:09:39 25 4
gpt4 key购买 nike

我不禁质疑在大型系统中使用可区分联合是否违反了打开/关闭原则。

我了解打开/关闭原则是面向对象的而不是功能性的。但是,我有理由相信存在相同的代码气味。

我经常避免使用 switch 语句,因为我通常被迫处理最初没有考虑到的情况。因此,我发现自己必须使用新案例和一些相关行为来更新每个引用。

因此,我仍然相信可区分联合与 switch 语句具有相同的代码气味。

我的想法准确吗?

为什么 switch 语句不受欢迎,但受歧视的工会被接受?

随着代码库的发展或离题,我们使用可区分联合是否不会遇到与切换语句相同的维护问题?

最佳答案

在我看来,Open/Closed 原则有点模糊——“open for extension”到底是什么意思?

这是否意味着扩展新数据,或扩展新行为,或两者兼而有之?

这是 Betrand Meyer 的引述(取自 Wikipedia):

A class is closed, since it may be compiled, stored in a library, baselined, and used by client classes. But it is also open, since any new class may use it as parent, adding new features. When a descendant class is defined, there is no need to change the original or to disturb its clients.



这是罗伯特·马丁的 article 的引述。 :

The open-closed principle attacks this in a very straightforward way. It says that you should design modules that never change. When requirements change, you extend the behavior of such modules by adding new code, not by changing old code that already works.



我从这些引用中得到的重点是永远不要破坏依赖你的客户。

在面向对象的范式(基于行为的)中,我会将其解释为使用接口(interface)(或抽象基类)的建议。那么,如果
需求改变,你要么创建一个现有接口(interface)的新实现,或者,如果需要新的行为,创建一个扩展的新接口(interface)
原来的。 (顺便说一句,switch 语句不是面向对象的—— you should be using polymorphism!)

在功能范式中,从设计的角度来看,接口(interface)的等价物是功能。就像在 OO 设计中将接口(interface)传递给对象一样,
您可以将一个函数作为参数传递给 FP 设计中的另一个函数。
更重要的是,在 FP 中,每个函数签名都自动成为一个“接口(interface)”!功能的实现可以在以后更改,只要
它的函数签名不会改变。

如果确实需要新的行为,只需定义一个新函数——旧函数的现有客户端不会受到影响,而客户端
需要这个新功能的人需要修改以接受新参数。

扩展 DU

现在,在 F# 中更改 DU 需求的特定情况下,您可以通过两种方式扩展它而不影响客户端。
  • 使用组合从旧数据类型或
  • 构建新数据类型
  • 对客户隐藏案例并使用事件模式。

  • 假设你有一个像这样的简单 DU:
    type NumberCategory = 
    | IsBig of int
    | IsSmall of int

    并且您想添加一个新案例 IsMedium .

    在组合方法中,您将创建一个新类型而不触及旧类型,例如:
    type NumberCategoryV2 = 
    | IsBigOrSmall of NumberCategory
    | IsMedium of int

    对于只需要原始 NumberCategory 的客户组件,您可以像这样将新类型转换为旧类型:
    // convert from NumberCategoryV2 to NumberCategory
    let toOriginal (catV2:NumberCategoryV2) =
    match catV2 with
    | IsBigOrSmall original -> original
    | IsMedium i -> IsSmall i

    您可以将其视为一种明确的向上转型:)

    或者,您可以隐藏案例并仅公开事件模式:
    type NumberCategory = 
    private // now private!
    | IsBig of int
    | IsSmall of int

    let createNumberCategory i =
    if i > 100 then IsBig i
    else IsSmall i

    // active pattern used to extract data since type is private
    let (|IsBig|IsSmall|) numberCat =
    match numberCat with
    | IsBig i -> IsBig i
    | IsSmall i -> IsSmall i

    稍后,当类型更改时,您可以更改事件模式以保持兼容:
    type NumberCategory = 
    private
    | IsBig of int
    | IsSmall of int
    | IsMedium of int // new case added

    let createNumberCategory i =
    if i > 100 then IsBig i
    elif i > 10 then IsMedium i
    else IsSmall i

    // active pattern used to extract data since type is private
    let (|IsBig|IsSmall|) numberCat =
    match numberCat with
    | IsBig i -> IsBig i
    | IsSmall i -> IsSmall i
    | IsMedium i -> IsSmall i // compatible with old definition

    哪种方法最好?

    好吧,对于我完全控制的代码,我什么都不会使用——我只会对 DU 进行更改并修复编译器错误!

    对于作为 API 公开给我无法控制的客户端的代码,我会使用主动模式方法。

    关于design-patterns - 有区别的工会是否与开放关闭原则相冲突,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34597685/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com