3

鲍勃大叔:函数式编程真的不需要面向对象吗?

 1 year ago
source link: https://www.jdon.com/64579.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

鲍勃大叔:函数式编程真的不需要面向对象吗?


什么是类?根据字典,一个类是:

一组、集合、群体或配置,其中的成员被认为具有某些共同的属性或特征;一个种类或类别。

现在在阅读下一段时考虑一下这个定义:

在OO语言中,我们将我们的程序组织成具有类似特征的对象类别。我们用它们共同的属性和行为来描述这些对象。我们努力创造分类的层次,使这些对象能够适应其中。我们认为更高层次的分类是抽象的,允许表达独立于不相关细节的一般真理。(事实上,我曾经把抽象定义为:放大基本的东西,消除不相关的东西)。
banq:偏见是抽象的代名词

1966年,分类(classification)抽象的力量促使Simula的作者们创造了关键词“类class”。1980年,Bjarne Stroustrup延续了这一惯例,在C++中使用了“类class”这个关键词。这实际上有些奇怪,因为C语言已经有了关键词struct,其含义几乎完全相同。但是,“类”这个词的威力是很强的。

在90年代中期,这个词的力量使Java(然后是C#)的作者宣布并强制要求程序中的一切都必须是“类class”的一部分。这是一个戏剧性的过度行为。在我看来,一些被Java强行纳入“类”的东西根本就不应该出现在“类”中。例如,java.lang.Math这个类实际上只是一批函数的命名空间,在任何意义上都不是一个对象的分类。

这种将对象分类和命名空间混为一谈的做法是令人困惑的,也是不必要的。

Java(以及延伸到C#)的另一个过度是,方法默认是多态的。多态性是一种工具,而不是一种规则。许多(如果不是大多数)函数调用都不需要动态调度

这些种类的过度导致了对类的真正含义的混淆。

所以,让我们开门见山吧:

软件设计的最古老的规则之一是:我们应该将系统的元素划分为松散耦合和内部凝聚的元素(banq:低关联高内聚)。这些元素成为我们可以放置数据和行为的命名良好的地方。这种做法遵循了一个古老的谚语:每件事都有一个地方,每件事都有它的位置。

这些元素是什么?似乎很明显,对象的分类结构应该在列表中占据重要位置。像java.lang.Math这样的命名的函数库是另一个明显的选择。

  • 在一种情况下,我们有一批操纵内部数据结构的函数。
  • 在另一种情况下,我们有一批操纵外部数据结构的函数。

这些元素或这批函数的基本特征:它们都是内部凝聚的。
这意味着这批函数中的所有函数都是相互紧密联系的,因为它们操作的是相同的数据结构,无论是内部还是外部。正是这种内聚性推动了软件设计的分区。

# # # 例子
最近我在写一个叫more-speech的应用程序,它是一个浏览nostr网络上消息的客户端。这个新工作是由中继器组成的,它们使用简单的websocket协议来向客户端传输消息。more-speech客户端是用Clojure编写的,这是一种函数编程语言。

在早期,我创建了一个名为protocol的模块,以容纳实现nostr protocol 的代码。在这个模块中,我首先管理了信息所经过的websockets,然后根据协议的规则对这些信息进行解码和处理。

Clojure不是一个传统的OOPL,没有用于声明和定义对象以及操作对象的方法的类关键词。相反,Clojure中的模块只是一批没有在语法上与任何特定数据绑定的函数。因此,我的protocol 模块有处理WebSockets的函数、处理消息的函数和处理protocol 元素的函数。它们是有凝聚力的,因为它们都与nostr协议有关;但没有中心数据结构来统一它们。

有一天,我意识到我缺少一个抽象的东西。nostr protocol 确实可能会通过websockets传输的,但这个protocol 规则却与websockets毫无关系。这些规则处理的是借用websockets实现传输数据,而不是依赖websockets本身,然而,我的协议模块却充斥着websocket的代码。

因此,我通过创建一个名为relay的抽象,将websocket代码与protocol 代码分开:

  • relay是一个数据结构,它包含websocket的url 、websocket本身,以及一个在收到消息时调用的函数。
  • relay的数据结构由诸如make、open、close和send等函数来操作。

这个relay模块非常清楚地定义了一个对象的类别:
而nostr protocol 为活动中的relay列表中的每个URL构建一个中继对象,它打开这些relay并向它们发送消息,收到的消息通过传递到构建relay对象的函数中的回调函数发送至protocol 。
为了保持函数式编程的不变性和参考透明度约束,更新relay状态的函数会返回该relay的新实例。

##教训
Java、C#、Ruby和C++都强制要求或者强行鼓励将系统划分为“类class”,Clojure则不然,它对类完全不了解。

我从protocol 和relay中得到的教训是,在编写复杂的Clojure程序时,我没有对类结构给予足够的重视。相反,我一直让函数或多或少地在模块中积累,类似于用C、Fortran、Basic甚至Assembler编程的方式。

而这是一种懒惰。

对象存在于程序中,它们可以而且应该被分类。

所以,从现在开始,我将会更加关注我的系统中的对象的分类结构。

每样东西都有一个位置,每样东西都有它的位置。

原文点击标题


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK