Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Design patterns 受歧视的工会是否与开合原则相冲突_Design Patterns_F#_Discriminated Union - Fatal编程技术网

Design patterns 受歧视的工会是否与开合原则相冲突

Design patterns 受歧视的工会是否与开合原则相冲突,design-patterns,f#,discriminated-union,Design Patterns,F#,Discriminated Union,我不禁怀疑,在一个大系统中使用歧视性工会是否违反了开放/封闭原则 我理解开/关原则是面向对象的,而不是功能性的。然而,我有理由相信同样的代码气味也存在 我经常避免使用switch语句,因为我通常被迫处理最初没有说明的案例。因此,我发现自己必须用一个新的案例和一些相关的行为来更新每个引用 因此,我仍然相信,受歧视的工会与switch语句具有相同的代码气味 我的想法准确吗 为什么不赞成交换声明,但接受歧视的工会 当代码库发展或偏离时,我们是否会遇到使用歧视联合的维护问题,就像我们切换语句一样 对象和

我不禁怀疑,在一个大系统中使用歧视性工会是否违反了开放/封闭原则

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

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

因此,我仍然相信,受歧视的工会与switch语句具有相同的代码气味

我的想法准确吗

为什么不赞成交换声明,但接受歧视的工会


当代码库发展或偏离时,我们是否会遇到使用歧视联合的维护问题,就像我们切换语句一样

对象和受歧视的联合具有相互双重的限制:

  • 在使用接口时,添加实现接口的新类很容易,而不会影响其他实现,但添加新方法很难(即,如果添加新方法,则需要将该方法的实现添加到实现接口的每个类)
  • 在设计DU类型时,很容易使用该类型添加新方法,而不会影响其他方法,但很难添加新案例(即,如果添加新案例,则需要更新所有现有方法来处理它)
因此,DU肯定不适合对所有问题进行建模;但传统的面向对象设计也不是。通常,您知道将来需要在哪个“方向”进行修改,因此很容易选择(例如,列表肯定是空的,或者有一个头部和一个尾部,因此通过DU对其进行建模是有意义的)

有时,您希望能够在两个方向上扩展事物(添加新的“种类”对象,也添加新的“操作”)——这与相关,并且在经典OO编程或经典FP编程中都没有特别干净的解决方案(虽然有点巴洛克式的解决方案是可能的,但请参见Vesa Karvonen的评论,我将其翻译为F#)


DU可能比switch语句更受欢迎的一个原因是F#编译器对穷举性和冗余检查的支持可能比C#编译器对switch语句的检查更彻底(例如,如果我有
将x与|A->'A'| B->'B'
匹配,并且我添加了一个新的DU case
C
那么我将得到一个警告/错误,但是在C#中使用
枚举
时,我仍然需要一个
默认值
case,这样编译时检查就不会那么强).

我不确定你用OO实现开闭原则的方法是什么,但我通常会通过使用来实现这样的原则性代码,我采用的另一种方法是使用接口。我倾向于避免基类

对于DU,您可以使用相同的方法,方法是在其他更硬编码的有用情况(如以下情况)之上添加一个open for extension case,其中有一个functor作为参数:

类型案例=
|弦的情况1
|int案例2
|IFoo案例3
|打开机箱(单元->T)
当使用OpenCase时,您可以传递一个函数,该函数特定于您实例化此判别联合值的站点

为什么不赞成交换声明,但接受歧视的工会

您可以将DU与模式匹配进行比较,因此我将尝试澄清:

模式匹配是一种代码构造(如
开关
),而DU是一种类型构造(如类或结构的封闭层次结构或枚举)

在F#中使用
match
的模式匹配比在C#中使用
switch
的能力更强

当代码库发展或偏离时,我们是否会遇到使用歧视联合的维护问题,就像我们切换语句一样

与模式匹配一起使用的区分联合比常规switch语句具有更多的类型安全性/穷尽性属性,编译器更有用,因为它将对不完整的匹配发出警告,而这是使用C#中的switch语句无法得到的


您可能会对OO代码有维护方面的顾虑,它是开放-关闭原则,我觉得DU与此无关。

在我看来,开放/关闭原则有点模糊——“开放-扩展”实际上是什么意思

它是指用新数据扩展,还是用新行为扩展,或者两者兼而有之

以下是来自Betrand Meyer(摘自):

类是封闭的,因为它可以被编译、存储在库中、基线化并由客户机类使用。 但它也是开放的,因为任何新类都可以使用它作为父类,添加新功能。 定义子类时,无需更改原始类或干扰其客户端。

这里引用罗伯特·马丁的一句话:

开闭原则以一种非常简单的方式来攻击这一点,它说你应该设计 永不改变的模块。当需求改变时,您可以扩展这些模块的行为 通过添加新代码,而不是更改已经运行的旧代码来创建模块

我从这些引语中得到的是强调永远不要破坏依赖你的客户

在面向对象的范例(基于行为)中,我将其解释为使用接口(或抽象基类)的建议 当需求发生变化时,您可以创建现有接口的新实现,或者,如果需要新的行为,可以创建扩展的新接口 原来的一个。(顺便说一句,switch语句不是OO-!)

在功能范式中,从设计的角度来看,接口的等价物是一个函数。就像在OO设计中将接口传递给对象一样, 您可以将一个函数作为参数传递给中的另一个函数
type NumberCategory = 
    | IsBig of int 
    | IsSmall of int 
type NumberCategoryV2 = 
    | IsBigOrSmall of NumberCategory 
    | IsMedium of int 
// 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