首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >F#设计模式

F#设计模式
EN

Stack Overflow用户
提问于 2014-10-29 14:13:24
回答 4查看 988关注 0票数 1

在使用C#进行了10多年的面向对象编程之后,当我学习F#时,我很难想象如何以模块化的方式设计应用程序,这样就可以添加功能而无需修改已经存在的代码。

您将如何动态地将案件添加到受歧视的工会?

当文件中的顺序重要时,如何在程序集中划分代码?

例如,策略模式如何适合于F#?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2014-10-29 17:28:08

这个问题有五个部分,其中一些尚未得到答复。

.设计模式(一般情况下):您可以在F#中实现众所周知的正式设计模式,就像在C#或VB.NET (GOF设计模式、企业架构模式、云设计模式等)中一样。在某些情况下,在F#中实现正式的设计模式并不是必要的,因为该语言具有使模式过时的内置特性。在这方面,一个经常被引用的例子是访客模式。然而,在C#或VB.NET中也可以用功能实现取代正式的设计模式(但不那么优雅)。另一方面,函数式语言有自己的“模式”,但它们通常不被称为“模式”,因为它们往往只是算法方法,而形式化程度较低。

.允许在不修改已经存在的代码的情况下添加功能--参见@kvb的答案。

动态添加案例 ->参见@kvb的答案。

当文件中的顺序重要时,在程序集中划分代码:此限制有助于编写路径简单易懂的代码。在C#/VB.NET中实际上鼓励相互依赖,这增加了代码的复杂性,并使代码“虚幻”。然而,在某些情况下,相互依赖的类似结构是合法的。您可以通过以下四种方式在F#中定义它们:

  1. 使用类类型。无论声明的顺序如何,同一个类的成员都可以相互调用。
  2. 使用and关键字使函数、值或类型(在同一个文件中)相互依赖。
  3. 使用内部类型扩展在定义其他类型之前和之后定义类型的部分(在同一个文件中)
  4. 使用分离的接口模式打破相互依赖关系。

.策略模式 ->也见@plinth的答案。你可以用GOF的方式来做,就像在C#或VB.NET中一样,也可以用F#、C#或VB.NET (它们都是函数式语言)的函数式方式来完成。然而,F#更简洁/更有表现力,并具有其他功能特性,如功能组合、部分功能应用等:

代码语言:javascript
复制
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
票数 3
EN

Stack Overflow用户

发布于 2014-10-29 14:53:37

由于F#中的函数是可操作的和可组合的,所以这种策略模式是无处不在的。

例如,假设我有一个点和路径元素的类型定义:

代码语言:javascript
复制
type Point = { X:float; Y:float }
type PathOp =
| Close
| MoveTo of Point
| LineTo of Point
| CurveTo of Point * Point * Point

和功能类型:

代码语言:javascript
复制
type PointMutator = Point->Point
type PathOpMutator = PathOp->PathOp

注意:在这种情况下,mutator是一个函数,给定一个对象返回相同类型的对象,并以某种方式更改内容(可能)。我可以编写一个对象,在如下的路径序列中更改元素:

代码语言:javascript
复制
let pathMutator (mutator:PathOpMutator) path =
    path |> Seq.map(mutator)

它创建了一个新的路径操作序列,创建了每个路径的突变。

现在,我可以这样做:

代码语言:javascript
复制
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))

因此,如果我想编写代码来偏移路径的全部内容,我将访问路径中应用偏移策略的每个元素:

代码语言:javascript
复制
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语言进行互操作,您可以通过接口来实现这一点,如果您真的需要的话:

代码语言:javascript
复制
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模式中的一件事情,如果您想要精确的话,这可能会使问题更加严重--许多模式重叠,您的实际实现可能没有可靠的分类法。

票数 2
EN

Stack Overflow用户

发布于 2014-10-29 14:26:42

这个问题有点笼统,但我可以尝试回答更具体的问题:

  1. 在编译时知道一组可能的受歧视的联合情况是非常有帮助的,所以编译器可以在您的代码没有处理可能的情况时进行检查。
  2. 有一个很好的资源可以讨论F#程序的设计,我认为它可以回答您的问题:http://fsharpforfunandprofit.com/posts/recipe-part3/
  3. 很好的答案可以在这里找到:Strategy pattern in F#
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26632586

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档