15

Clojure 语言的创建者声称“开放的大型函数集在开放的小型可扩展抽象集上运行是算法重用和库互操作性的关键”。显然,它与典型的 OOP 方法相矛盾,在这种方法中,您创建了许多抽象(类)和一组相对较小的在它们上运行的函数。请推荐一本书、书中的一章、一篇文章或您的个人经历,详细说明这些主题:

  1. 激励 OOP 中出现的问题的示例,以及如何使用“在少数抽象上使用多个函数”来解决这些问题
  2. 如何有效地进行 MFUFA* 设计
  3. 如何重构面向 MFUFA 的 OOP 代码
  4. OOP 语言的语法如何妨碍 MFUFA

*MFUFA:“基于少量抽象的许多功能”

4

4 回答 4

18

编程中有两个主要的“抽象”概念:

  1. 参数化(“多态性”,泛型)。
  2. 封装(数据隐藏),

[编辑:这两个是双重的。第一个是客户端抽象,第二个是实现者端抽象(如果您关心这些事情:就形式逻辑或类型论而言,它们分别对应于普遍存在量化)。

在 OO 中,类是实现这两种抽象的厨房水槽功能。

广告 (1),对于几乎每个“模式”,您都需要定义一个自定义类(或多个)。另一方面,在函数式编程中,您通常有更轻量级和直接的方法来实现相同的目标,特别是函数和元组。例如,经常有人指出,GoF 中的大多数“设计模式”在 FP 中都是多余的。

广告 (2),如果您没有需要检查的可变状态在各处徘徊,则需要较少的封装。您仍然在 FP 中构建 ADT,但它们往往更简单、更通用,因此您需要的数量更少。

于 2012-05-12T16:41:22.163 回答
9

当您以面向对象的风格编写程序时,您会强调用数据类型来表达领域。乍一看,这似乎是个好主意——如果我们与用户一起工作,为什么不开设课程User呢?如果用户买卖汽车,为什么不上课Car呢?这样我们就可以轻松地维护数据和控制流——它只是反映了现实世界中事件的顺序。虽然这对于领域对象来说非常方便,但对于许多内部对象(即不反映来自现实世界的任何东西,但只出现在程序逻辑中的对象)来说,它并不是那么好。也许最好的例子是 Java 中的一些集合类型。在 Java(和许多其他 OOP 语言)中,有两个数组,Lists。在 JDBC 中有ResultSet这也是一种集合,但不实现Collection接口。对于输入,您将经常使用InputStream它为顺序访问数据提供接口 - 就像链表一样!但是它也没有实现任何类型的集合接口。因此,如果您的代码与数据库一起使用并使用ResultSet它,那么将其重构为文本文件和InputStream.

MFUFA 原则告诉我们少关注类型定义,多关注常见抽象。出于这个原因,Clojure 为所有提到的类型引入了单一抽象 - 序列。任何可迭代对象都会自动强制进行排序,流只是惰性列表,结果集可以轻松转换为以前的类型之一。

另一个例子是使用PersistentMap结构和记录的接口。有了这样的通用接口,创建可重用的子例程就变得非常容易,并且不需要花费大量时间进行重构。

总结并回答您的问题:

  1. OOP 中经常出现的问题的一个简单示例:从许多不同的来源(例如数据库、文件、网络等)读取数据并以相同的方式处理它。
  2. 为了做出好的 MFUFA 设计,尽量使抽象尽可能通用并避免临时实现。例如,避免类型 a-la UserList-List<User>在大多数情况下就足够了。
  3. 遵循第 2 点的建议。此外,尝试向您的数据类型(类)添加尽可能多的接口。例如,如果你真的需要UserList(例如当它应该有很多额外的功能时),在它的定义中添加ListIterable接口。
  4. OOP(至少在 Java 和 C# 中)不太适合这一原则,因为它们在初始设计时试图封装整个对象的行为,因此很难向它们添加更多功能。在大多数情况下,您可以扩展有问题的类并将所需的方法放入新对象中,但是 1)如果其他人实现了他们自己的派生类,它将与您的不兼容;2) 有时类final或所有字段都已创建private,因此派生类无法访问它们(例如,向类添加新功能String应实现附加类StringUtils)。尽管如此,我上面描述的规则使得在 OOP 代码中使用 MFUFA 变得更加容易。最好的例子是 Clojure 本身,它以 OO 风格优雅地实现,但仍然遵循 MFUFA 原则。

UPD。我记得对面向对象和函数式风格之间差异的另一种描述,这可能更好地总结了我上面所说的一切:以OO风格设计程序是根据数据类型(名词)来思考,而以函数式设计是根据操作来思考(动词)。您可能会忘记一些名词是相似的(例如忘记继承),但您应该始终记住许多动词在实践中做同样的事情(例如具有相同或相似的接口)。

于 2012-05-12T21:13:41.100 回答
6

引用的更早版本:

“列表的简单结构和自然适用性反映在令人惊讶的非异质性函数中。在 Pascal 中,过多的可声明数据结构会导致函数内部出现专门化,从而抑制和惩罚随意合作。最好让 100 个函数在一个数据结构上运行而不是让 10 个函数对 10 个数据结构进行操作。”

...来自著名的SICP书的前言。我相信这本书有很多关于这个主题的适用材料。

于 2012-05-12T18:17:21.617 回答
0

我认为您没有意识到图书馆和程序之间存在差异。

运行良好的 OO 库通常会生成少量抽象,程序使用这些抽象来为其领域构建抽象。较大的 OO 库(和程序)使用继承来创建不同版本的方法并引入新方法。

所以,是的,同样的原则也适用于 OO 库。

于 2012-05-12T15:51:01.173 回答