gpt4 book ai didi

c# - 函数式编程和解耦

转载 作者:行者123 更新时间:2023-12-04 04:28:12 26 4
gpt4 key购买 nike

我是您的经典 OOP 开发人员。然而,自从我发现纯函数式编程语言以来,我一直对为什么 OOP 似乎以合理的方式解决大多数业务案例很感兴趣。
在我的软件开发经验中,我现在已经到了寻求更简洁和富有表现力的语言的地步。我通常用 C# 编写我的软件,但对于我的最新项目,我决定采取飞跃并使用 F# 构建业务服务。在这样做的过程中,我发现很难理解如何使用纯功能方法完成解耦。
情况是这样的。我有一个数据源,即 WooCommerce,但我不想将我的函数定义绑定(bind)到该特定数据源。
在 C# 中,对我来说很明显我想要一个看起来像这样的服务

public record Category(string Name);

public interface ICategoryService
{
Task<IEnumerable<Category>> GetAllAsync();
}

// With a definition for the service that specifies WooCommerce
public class WcCategoryService : ICategoryService
{
private readonly WCRestEndpoint wcRest;

// WooCommerce specific dependencies
public WcCategoryService(WCRestEndpoint wcRest)
{
this.wcRest = wcRest;
}

public Task<IEnumerable<Category>> GetAllAsync()
{
// Call woocommerce REST and map the category to our domain category
}
}
现在将来,如果当我决定我们需要一个新的存储来提供类别时,我可以为该特定服务定义一个新的实现,替换注入(inject)的类型并且不会因为这种变化而搞乱依赖项。
试图了解函数依赖方法是如何解决的,我遇到了这种情况(阅读“Domain Modeling made functional”),其中类型签名直接定义依赖关系,因此上面的 C# 等价物将变成高度耦合的定义
type Category = { Name: string }
type GetCategories =
WCRestEndpoint
-> Category list
突然间,如果我要更改类别的来源,我将不得不更改功能签名或提供要使用的新定义,这会在应用程序中产生涟漪效应,因此不是很健壮。
我很好奇的是我是否误解了一些基本的东西。
用我的 OOP 大脑,我能想到的就是这样
type Category = { Name: string }

// No longer directly dependent on WCRestEndpoint
type GetCategories = unit -> Category list

// But the definition would require scoped inclusion of the dependency
// Also how does the configuration get passed in without having the core library be dependent on the Environment or a config in the assembly?
let rest = WCRestEndpoint(/* Config... */)

type getCategories: GetCategories =
fun () ->
let wcCategories = rest.GetCategories()
// Convert the result into a Category type
我环顾四周,没有找到任何关于如何使用纯功能方法处理更改的解释,这让我相信我误解了一些基本的东西。
如何在不将函数类型签名绑定(bind)到实现特定类型的情况下公开函数式 API?我想错了吗?

最佳答案

在我意识到我以错误的方式看待它之前,我多年来一直在为这个问题苦苦挣扎。来自面向对象的开发和依赖注入(inject),我一直在寻找依赖注入(inject)的功能替代方案。我终于意识到Dependency Injection makes everything impure ,这意味着如果您想申请 not even partial application,则不能使用该方法( a functional architecture ) .
红鲱鱼是专注于依赖关系。相反,专注于编写纯函数。您仍然可以使用 Dependency Inversion Principle ,但不要专注于行动和交互,而是专注于数据。如果函数需要一些数据,请将其作为参数传递。如果函数必须做出决定,return it as a data structure .
你没有提供任何你想在哪里使用 Category 列表的例子。值,但依赖于此类数据的函数将具有如下类型:

Category list -> 'a
这样的功能与类别的来源完全解耦。它只取决于 Category类型本身,它是领域模型的一部分。
最终,您需要从某个地方获取类别,但这项工作您会推到系统的边界,例如 Main :
let Main () =
let categories = getCategories ()
let result = myFunction categories
result
因此,如果您改变了如何获取类别的想法,您只需更改一行代码。这种架构是 akin to a sandwich ,在应用程序的纯心周围有不纯的 Action 。它也被称为 functional core, imperative shell .

关于c# - 函数式编程和解耦,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68161072/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com