因此,从概念上讲,我们正在讨论两种完全不同的域建模方法。
考虑我们在文章中看到的函数式方法:
type Shape = // define a "union" of alternative structures
| Circle of radius:int
| Rectangle of height:int * width:int
| Point of x:int * y:int
| Polygon of pointList:(int * int) list
let draw shape = // define a function "draw" with a shape param
match shape with
| Circle radius ->
printfn "The circle has a radius of %d" radius
| Rectangle (height,width) ->
printfn "The rectangle is %d high by %d wide" height width
| Polygon points ->
printfn "The polygon is made of these points %A" points
| _ -> printfn "I don't recognize this shape"
这里的关键点是Shape 定义了四个可能的选项:Circle、Rectangle、Polygon 和Point。
我不能在我的程序的其他地方发明一个新的联合案例,Shape 被严格定义为这些选项之一,当模式匹配时,编译器可以检查我没有错过任何一个。
如果我使用 C# 样式模型:
interface IShape {}
class Circle : IShape {}
class Rectangle : IShape {}
class Point : IShape {}
class Polygon : IShape {}
可能的类型是无限的。在一个或多个其他文件中,如果我愿意,我可以简单地定义更多:
class Triangle : IShape {}
class Pentagon : IShape {}
class Hexagon : IShape {}
您永远无法知道可能存在多少IShapes。
我们上面定义的 F# Shape 不是这样。它有四个选项,只有四个。
区分联合模型实际上非常强大,因为通常,当我们在软件中对域进行建模时,该域中的可能状态实际上是一组相对较小且简洁的选项。
让我们以 F# for Fun and Profit 网站的购物车为例:
type ShoppingCart =
| EmptyCart
| ActiveCart of unpaidItems : string list
| PaidCart of paidItems : string list * payment: float
如果我以这种方式为我的购物车建模,我将大大减少可能的无效状态的范围,因为我的购物车可能处于这三种状态之一,而不会处于其他状态。
接口和类可以让您对完全相同的状态进行建模,但它们不会阻止您创建任意数量的额外状态,这些状态完全无意义且与您的领域无关。