【发布时间】:2011-04-08 06:25:42
【问题描述】:
我最近一直在使用 F#,并尝试以函数式方式编写代码,而不是使用不同的语法重新进行 OOP。我现在遇到了一个问题,我可以通过混合继承和有区别的联合来解决这个问题,但我正试图找到一个纯函数式的样式表示。
我想要建模的是这样的(更改为保留模式,因为我无法使用实际代码):
type Shape =
| Rectangle of Size * Size
| Circle of Diameter
到目前为止一切顺利,但现在我需要表示与不同类型形状相关的附加属性集合,例如:
type ShapeProperty =
| Color of Shape * Color // Fine, valid for all shapes
| Rotation of Shape * Angle // Wants to be Rotation of Rectangle * Angle
| Details of Shape * int // Wants to be Detail of Circle * int
如果不是对 Shape 使用区分联合,而是使用基类和继承,我可以引用实际类型并确保旋转只能应用于 Rectangle 而不是 Circle,但现在我不能.有没有一种方法可以在保持纯函数数据结构的同时实现类似的东西?
编辑:
我目前的解决方案是将单个形状的定义与形状完全相关的事实分开,如下所示:
type Rectangle = Rectangle of Size * Size // Or using a record type
type Circle = Circle of Diameter // Or using a record type
type Shape = RectangleShape of Rectangle | CircleShape of Circle
这意味着我可以在 ShapeProperty 中引用类型:
type ShapeProperty =
| Color of Shape * Color
| Rotation of Rectangle * Angle
| Details of Circle * int
这感觉有点笨拙,因为现在需要将每个形状封装在 Shape 类型中以将它们存储在一个集合中,但它确实为我提供了一种表达我所追求的类型安全的方法。欢迎对此进行任何改进。
【问题讨论】:
-
您打算如何使用 ShapeProperty 类型?我认为您需要在这里进行不同的设计,但是如果没有更多细节,很难说哪种设计是合适的。
-
我需要 ShapeProperty(和其他类似的类)来添加各种形状的属性,而不必触及形状本身的定义,因为在我的真实案例中,这些属性仅在使用形状时才相关在某些情况下,但不是所有其他情况。基本上我需要一种类型安全的表示,它可以让我进行转换和查询,例如“查找所有旋转 10 的形状”。
-
这是什么意思?圆的旋转是否小于 10? (圆圈没有你描述的旋转的概念。)我认为你需要更多关于你想要什么的细节。
-
这个例子绝对不是完美的,我的意思是找到所有旋转 10 的形状是对一组 ShapeProperty 项目执行搜索,选择每个属性(以及每个形状)预期的类型和预期的值。在我的示例中,圆没有旋转,因为旋转圆(围绕其中心)不会改变它,在实际代码中,如果类型不支持“形状”,则这些属性显然根本不适用。
-
首先,围绕中心旋转一个圆圈可能会改变它,但我猜这是示例的缺陷,所以我将忽略它。其次,作为一个试图做你想做的事情的人,我可以告诉你这是一个坏主意。对于所有标签(这是 LSP 的变体),可区分联合的属性必须为真,否则它们不属于同一个联合。我会警告你,当我按照你想出的方式做这件事时,我正在与类型系统作斗争(矩形是矩形,让我将它作为参数传递而无需装箱)。
标签: inheritance f# functional-programming types