“许多功能在很少抽象的基础上运行”原则与OOP相比

时间:2012-05-12 15:41:26

标签: oop clojure functional-programming reusability

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

  1. 激发OOP中出现的问题的例子以及如何使用“很少抽象的许多函数”来解决这些问题
  2. 如何有效地进行MFUFA *设计
  3. 如何重构针对MFUFA的OOP代码
  4. OOP语言的语法如何影响MFUFA
  5. * MFUFA:“很少抽象的功能”

4 个答案:

答案 0 :(得分:18)

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

  1. 参数化(“多态”,通用性)。
  2. 封装(数据隐藏),
  3. [编辑:这两个是双重的。第一个是客户端抽象,第二个实现者端抽象(如果你关心这些事情:在形式逻辑或类型理论方面,它们对应于通用存在量化(分别)。]

    在OO中,该类是用于实现两种抽象的厨房接收器功能。

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

    Ad(2),如果你没有可变状态在你需要检查的所有地方挥之不去,那么封装需要少一点。你仍然在FP中构建ADT,但它们往往更简单,更通用,因此你需要的更少。

答案 1 :(得分:9)

当您以面向对象的方式编写程序时,您会强调在数据类型方面表达域区域。乍一看,这看起来是个好主意 - 如果我们与用户合作,为什么不上课User?如果用户出售和购买汽车,为什么不上课Car?通过这种方式,我们可以轻松维护数据和控制流程 - 它只反映现实世界中事件的顺序。虽然这对于域对象非常方便,但对于许多内部对象(即不反映现实世界中的任何内容但仅出现在程序逻辑中的对象)并非如此好。也许最好的例子是Java中的许多集合类型。在Java(以及许多其他OOP语言)中,有两个数组List。在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添加新函数,应该实现额外的类{ {1}})。尽管如此,我上面描述的规则使得在OOP代码中使用MFUFA变得更加容易。最好的例子是Clojure本身,它以OO风格优雅地实现,但仍然遵循MFUFA原则。
  5. UPD。我还记得面向对象和功能样式之间差异的另一种描述,可能总结得更好我上面所说的:用OO风格设计程序根据数据类型进行思考< / strong>(名词),而功能风格的设计是在操作方面的思考(动词)。你可能会忘记一些名词是相似的(例如忘记继承),但是你应该永远记住在实践中许多动词做同样的事情(例如有相同或类似的接口)。

答案 2 :(得分:6)

报价的早期版本:

“列表的简单结构和自然适用性反映在令人惊讶的非特异性函数中。在Pascal中,过多的可声明数据结构在函数内引入了一种特殊化,抑制和惩罚了偶然的合作。最好有100个函数在一个数据结构上运行,而不是在10个数据结构上运行10个函数。“

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

答案 3 :(得分:0)

我认为你没有得到图书馆和程序之间的区别。

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

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