在F#中实现构建器模式(la System.Text.StringBuilder)

在F#中实现构建器模式(la System.Text.StringBuilder),第1张

概述变异状态是构建模式的中心.有没有一种惯用的方法来实现F#中这样一个类的内部,它将减少/消除可变状态,同时保留通常的接口(这个类主要用于其他.NET语言)? 这是一个天真的实现: type QueryBuilder<'T>() = //' let where = ref None let orderBy = ref None 变异状态是构建器模式的中心.有没有一种惯用的方法来实现F#中这样一个类的内部,它将减少/消除可变状态,同时保留通常的接口(这个类主要用于其他.NET语言)?

这是一个天真的实现:

type queryBuilder<'T>() =                              //'    let where = ref None    let orderBy = ref None    let groupBy = ref None    member x.Where(cond) =        match !where with        | None -> where := Some(cond)        | _ -> invalIDOp "Multiple WHERE clauses are not permitted"    // members OrderBy and GroupBy implemented similarly

一种想法是创建一个记录类型来存储内部,并使用复制和更新表达式.

type private queryBuilderSpec<'T> =                     //'    { Where : ('T -> bool) option;                      //'      OrderBy : (('T -> obj) * bool) List;              //'      GroupBy : ('T -> obj) List }                      //'type queryBuilder<'T>() =                               //'    let spec = ref None    member x.Where(cond) =        match !spec with        | None ->             spec := Some({ Where = Some(cond); OrderBy = []; GroupBy = [] })        | Some({ Where = None; OrderBy = _; GroupBy = _} as s) ->             spec := Some({ s with Where = Some(cond) })        | _ -> invalIDOp "Multiple WHERE clauses are not permitted"    // members OrderBy and GroupBy implemented similarly

这一切似乎有点笨拙,也许在尝试在F#中实现命令式模式时应该预料到这一点.有没有更好的方法来做到这一点,再次,为了命令式语言保留通常的构建器界面?

解决方法 我认为根据您的用例,您可能会更好地使用不可变的实现.以下示例
将静态强制执行任何构建器在构建之前将其where,order和group属性设置为恰好一次,
虽然它们可以按任何顺序设置:

type queryBuilder<'t,'w,'o,'g> =   internal { where : 'w; order : 'o; group : 'g } withlet emptyBuilder = { where = (); order = (); group = () }let addGroup (g:'t -> obj) (q:queryBuilder<'t,_,unit>) : queryBuilder<'t,_> =  { where = q.where; order = q.order; group = g }let addOrder (o:'t -> obj * bool) (q:queryBuilder<'t,unit,_>) : queryBuilder<'t,_> =  { where = q.where; order = o; group = q.group }let adDWhere (w:'t -> bool) (q:queryBuilder<'t,_> =  { where = w; order = q.order; group = q.group }let build (q:queryBuilder<'t,'t->bool,'t->obj,'t->obj*bool>) =  // build query from builder here,kNowing that all components have been set

显然你可能必须针对你的特定约束调整它,并将它暴露给其他语言你可能想要使用另一个类上的成员和委托而不是let-bound函数和F#函数类型,但是你得到了图片.

UPDATE

也许值得扩展我已经完成的更多描述 – 代码有点密集.使用记录类型没有什么特别之处;一个普通的不可变类也一样好 – 代码会简洁一点,但与其他语言互 *** 作可能会更好.我的实现基本上有两个重要的特性

>每个添加方法都返回一个表示当前状态的新构建器.这是相当简单的,尽管它与Builder模式的正常实现方式明显不同.
>通过使用其他泛型类型参数,您可以强制执行非平凡的不变量,例如在使用Builder之前要求将几个不同属性中的每个属性指定一次.对于某些应用程序来说这可能有点过头了,而且有点棘手.它只能用于不可变的构建器,因为我们可能需要在 *** 作后返回具有不同类型参数的构建器.

在上面的示例中,类型系统将允许此 *** 作序列:

let query =   emtpyBuilder  |> addGroup ...  |> addOrder ...  |> adDWhere ...  |> build

而这个不会,因为它永远不会设置顺序:

let query =  emptyBuilder  |> addGroup ...  |> adDWhere ...  |> build

正如我所说,这可能对你的应用程序来说太过分了,但这只是因为我们使用的是不可变的构建器.

总结

以上是内存溢出为你收集整理的在F#中实现构建器模式(la System.Text.StringBuilder)全部内容,希望文章能够帮你解决在F#中实现构建器模式(la System.Text.StringBuilder)所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1085377.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-27
下一篇 2022-05-27

发表评论

登录后才能评论

评论列表(0条)

保存