假设我有一个这样的类型:
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
赞成每种方法的原因是什么?
答案 0 :(得分:3)
最终,这是一个领域建模判断调用,这里没有一个明确的正确答案。重要的是每种选择如何影响代码的可读性和可维护性。
相对于任何特定的业务逻辑代码,我倾向于使用静态成员来实现与类型高度“内聚”的功能。考虑一下Parse
函数或智能构造函数/工厂方法。通用的方法是,如果我要通过将类型移动到其他地方来重构代码,那肯定是我希望与其一起移动的功能。将它们作为静态成员还可以通过智能感知来帮助发现,因为您只需要知道类型的名称即可找到它们。
另一方面,我将使用一个模块来容纳代表某些抽象流程的业务逻辑,并且如果所讨论的功能某种程度上特定于该业务逻辑并且不太可能在其外部有用,那么我会在模块中使用某个函数,即使它仍然是特定于类型的。例如,出于遗留原因,仅作为该工作流程一部分有用的非常特定于目的的解析器将是一个让函数绑定的函数,而不是静态成员,因为使用该类型的其他客户端通常甚至都不知道该函数。
在您的情况下,如果可以在上下文中的多个不同模块中使用静态成员Next
,则可以使用它-如果能够循环使用Seasons
是定义Season
是什么的基本质量。
否则,如果您只有一个模块,例如WeatherPatterns
,它会根据季节变化来调整降雨量,那是您代码中唯一关心循环Seasons
的部分,那么我将其作为该模块中的函数。
答案 1 :(得分:2)
您的示例非常简单,因此这里的任何一种方法都可以。但是,想象添加更多带有Season参数的函数。现在,类型定义看起来非常混乱。另外,这些函数可能需要利用一些共享的值和函数,这些值和函数可以在Season模块中声明为私有。
答案 2 :(得分:0)
[name_of_module]Module
,例如SeasonModule
。next
插入模块,可以在不指定模块的情况下访问模块值和函数(例如,用Season.next
代替open
)。 (当然,可以使用RequireQualifiedAccess
属性禁止这样做。)