在使用C#进行了10多年的面向对象编程之后,当我学习F#时,我很难想象如何以模块化的方式设计应用程序,这样就可以添加功能而无需修改已经存在的代码。
您将如何动态地将案件添加到受歧视的工会?
当文件中的顺序重要时,如何在程序集中划分代码?
例如,策略模式如何适合于F#?
发布于 2014-10-29 17:28:08
这个问题有五个部分,其中一些尚未得到答复。
.设计模式(一般情况下):您可以在F#中实现众所周知的正式设计模式,就像在C#或VB.NET (GOF设计模式、企业架构模式、云设计模式等)中一样。在某些情况下,在F#中实现正式的设计模式并不是必要的,因为该语言具有使模式过时的内置特性。在这方面,一个经常被引用的例子是访客模式。然而,在C#或VB.NET中也可以用功能实现取代正式的设计模式(但不那么优雅)。另一方面,函数式语言有自己的“模式”,但它们通常不被称为“模式”,因为它们往往只是算法方法,而形式化程度较低。
.允许在不修改已经存在的代码的情况下添加功能--参见@kvb的答案。
动态添加案例 ->参见@kvb的答案。
当文件中的顺序重要时,在程序集中划分代码:此限制有助于编写路径简单易懂的代码。在C#/VB.NET中实际上鼓励相互依赖,这增加了代码的复杂性,并使代码“虚幻”。然而,在某些情况下,相互依赖的类似结构是合法的。您可以通过以下四种方式在F#中定义它们:
and关键字使函数、值或类型(在同一个文件中)相互依赖。.策略模式 ->也见@plinth的答案。你可以用GOF的方式来做,就像在C#或VB.NET中一样,也可以用F#、C#或VB.NET (它们都是函数式语言)的函数式方式来完成。然而,F#更简洁/更有表现力,并具有其他功能特性,如功能组合、部分功能应用等:
let stratAdd x y = x + y
let stratMul x y = x * y
let partialStratAdd = stratAdd 10
let partialStratMul = stratMul 20
let chosenStrat =
if Random().Next(1, 100) < 50 then partialStratAdd
else partialStratMul
chosenStrat 7 // Gets either 17 or 140发布于 2014-10-29 14:53:37
由于F#中的函数是可操作的和可组合的,所以这种策略模式是无处不在的。
例如,假设我有一个点和路径元素的类型定义:
type Point = { X:float; Y:float }
type PathOp =
| Close
| MoveTo of Point
| LineTo of Point
| CurveTo of Point * Point * Point和功能类型:
type PointMutator = Point->Point
type PathOpMutator = PathOp->PathOp注意:在这种情况下,mutator是一个函数,给定一个对象返回相同类型的新对象,并以某种方式更改内容(可能)。我可以编写一个对象,在如下的路径序列中更改元素:
let pathMutator (mutator:PathOpMutator) path =
path |> Seq.map(mutator)它创建了一个新的路径操作序列,创建了每个路径的突变。
现在,我可以这样做:
let pathPointMutator (mutator:PointMutator) op =
match op with
| Close -> Close
| MoveTo(pt) -> MoveTo(mutator(pt))
| LineTo(pt) -> LineTo(mutator(pt))
| CurveTo(cp1, cp2, dp) -> CurveTo(mutator(cp1), mutator(cp2), mutator(cp3))因此,如果我想编写代码来偏移路径的全部内容,我将访问路径中应用偏移策略的每个元素:
let pointOffseter offset pt =
{ X = offset.X + pt.X; Y = offset.Y + pt.Y }
let offsetPath offset = pathMutator (pathPointMutator (pointOffsetter offset))现在,我在这里大量使用部分函数应用程序--当我只将偏移量传递到pointOffsetter中时,我将得到一个Point->Point函数,它将向其参数中添加绑定偏移量。此函数又部分应用于pathPointMutator,后者返回PathOp->PathOp的函数,后者部分应用于pathMutator,返回Seq<PathOp>->Seq<PathOp>的函数。
本质上,我所做的是创建一种将策略应用于每个路径操作的方法,以便创建新的路径操作。
我不是使用接口来定义策略,而是使用强类型函数来定义策略。概念是一样的,但具体的实施是不同的。
如果您想要与其他.NET语言进行互操作,您可以通过接口来实现这一点,如果您真的需要的话:
type ICalculate =
abstract member Calculate : float->float->float
type Adder =
interface ICalculate with
member this.Calculate x y = x + y
type Subber =
interface ICalculate with
member this.Calculate x y = x - y
type Responder =
member private this.GetCalculator(op) =
match op with
| '+' = new Adder()
| '-' = new Subber()
| _ -> ivalidArg "op" "op not defined"
member this.Respond op x y =
let calc = GetCalculator(op)
calc x y当然,这是做计算的工作量太大了,但是您的注意力应该放在ICalculator的盲目使用上,而不知道它的实现细节。
现在,更准确地说,在策略定义中,Responder更准确,因为实际的策略被选择得比较晚,而在PathOp示例中,它是先验的。这是GoF模式中的一件事情,如果您想要精确的话,这可能会使问题更加严重--许多模式重叠,您的实际实现可能没有可靠的分类法。
发布于 2014-10-29 14:26:42
这个问题有点笼统,但我可以尝试回答更具体的问题:
https://stackoverflow.com/questions/26632586
复制相似问题