静态成员vs模块在F#中的类型?

时间:2018-12-30 16:39:29

标签: f#

假设我有一个这样的类型:

type Season =
| Spring
| Summer
| Autumn
| Winter

我想要一个函数next返回下一个季节:

let next s = 
  match s with
  | Spring -> Summer
  | Summer -> Autumn
  | Autumn -> Winter
  | Winter -> Spring

我可以在两个地方放置此功能。

在命名模块中:

module Season = 
  let next s = 
    match s with
    | Spring -> Summer
    | Summer -> Autumn
    | Autumn -> Winter
    | Winter -> Spring

或作为类型的静态成员:

type Season =
| Spring
| Summer
| Autumn
| Winter
  with 
    static member next s = 
      match s with
      | Spring -> Summer
      | Summer -> Autumn
      | Autumn -> Winter
      | Winter -> Spring

赞成每种方法的原因是什么?

3 个答案:

答案 0 :(得分:3)

最终,这是一个领域建模判断调用,这里没有一个明确的正确答案。重要的是每种选择如何影响代码的可读性和可维护性。

相对于任何特定的业务逻辑代码,我倾向于使用静态成员来实现与类型高度“内聚”的功能。考虑一下Parse函数或智能构造函数/工厂方法。通用的方法是,如果我要通过将类型移动到其他地方来重构代码,那肯定是我希望与其一起移动的功能。将它们作为静态成员还可以通过智能感知来帮助发现,因为您只需要知道类型的名称即可找到它们。

另一方面,我将使用一个模块来容纳代表某些抽象流程的业务逻辑,并且如果所讨论的功能某种程度上特定于该业务逻辑并且不太可能在其外部有用,那么我会在模块中使用某个函数,即使它仍然是特定于类型的。例如,出于遗留原因,仅作为该工作流程一部分有用的非常特定于目的的解析器将是一个让函数绑定的函数,而不是静态成员,因为使用该类型的其他客户端通常甚至都不知道该函数。

在您的情况下,如果可以在上下文中的多个不同模块中使用静态成员Next,则可以使用它-如果能够循环使用Seasons是定义Season是什么的基本质量。

否则,如果您只有一个模块,例如WeatherPatterns,它会根据季节变化来调整降雨量,那是您代码中唯一关心循环Seasons的部分,那么我将其作为该模块中的函数。

答案 1 :(得分:2)

您的示例非常简单,因此这里的任何一种方法都可以。但是,想象添加更多带有Season参数的函数。现在,类型定义看起来非常混乱。另外,这些函数可能需要利用一些共享的值和函数,这些值和函数可以在Season模块中声明为私有。

答案 2 :(得分:0)

  • 非咖喱的静态成员方法可以重载。模块功能不能。
  • C#仅将模块视为[name_of_module]Module,例如SeasonModule
  • 通过next插入模块,可以在不指定模块的情况下访问模块值和函数(例如,用Season.next代替open)。 (当然,可以使用RequireQualifiedAccess属性禁止这样做。)