由于在OOP中您可以将许多细节封装(隐藏)为类中的私有字段,因此您可以隐藏大部分细节。因此,当你想要改变某些东西(重构)时,它通常是#34;更容易,因为在大多数情况下,变更的范围将是有限的。
另一方面,在函数式编程中,如果要更改某些内容(添加字段或更改函数输入/输出),则必须在整个软件中查找该元素的每次出现并更新它们(有时)在软件框架的情况下,用户在当前代码库之外),这可能是不可能的,并导致向后不兼容的更改。
答案 0 :(得分:3)
起点
我们将从一个依赖于两个独立合同的小程序开始 - 配对和列表。只要合同履行
,这些合同的实施几乎可以是任何事情例如,配对合同提供cons
,head
和tail
- head(cons(a,b))
必须返回a
- 同样,tail(cons(a,b))
必须返回b
。
这种创建一组与数据交互的函数的技术称为数据抽象 - 如果您对该主题感兴趣,我在网站上有几个其他答案谈论它 - 链接在这篇文章的底部
// -------------------------------------------------
// pair contract
// head(cons(a,b)) == a
// tail(cons(a,b)) == b
const cons = (x,y) =>
[x,y]
const head = pair =>
pair[0]
const tail = pair =>
pair[1]
// -------------------------------------------------
// list contract
// list() == empty()
// list(a,b,c) == cons(a, cons(b, cons(c, empty())))
const empty = () =>
null
const list = (x,...xs) =>
x === undefined ? empty() : cons(x, list(...xs))
// -------------------------------------------------
// demo
const sum = xs =>
xs === empty()
? 0
: head(xs) + sum(tail(xs))
console.log(sum(list(1,2,3))) // 6

第一个重构:配对
现在我要通过重新实现cons
,head
和tail
来重构配对代码 - 请注意我们没有触及列表代码{{1} }或empty
,演示代码list
不需要更改
sum

第二个重构:列表
现在我要更改List实现,但仍然确保合同已经完成 - 请注意我没有必要更改Pair实现并且演示代码保持不变
// -------------------------------------------------
// pair contract
// head(cons(a,b)) == a
// tail(cons(a,b)) == b
const cons = (x,y) =>
f => f(x,y)
const head = pair =>
pair((x,y) => x)
const tail = pair =>
pair((x,y) => y)
// -------------------------------------------------
// list contract
// list() == empty()
// list(a,b,c) == cons(a, cons(b, cons(c, empty())))
const empty = () =>
null
const list = (x,...xs) =>
x === undefined ? empty() : cons(x, list(...xs))
// -------------------------------------------------
// demo
const sum = xs =>
xs === empty()
? 0
: head(xs) + sum(tail(xs))
console.log(sum(list(1,2,3))) // 6

一直打开......
我们实施的合同有效地封装了实施细节,就像OO程序中的私有数据/方法一样。请注意// -------------------------------------------------
// pair contract
// head(cons(a,b)) == a
// tail(cons(a,b)) == b
const cons = (x,y) =>
f => f(x,y)
const head = pair =>
pair((x,y) => x)
const tail = pair =>
pair((x,y) => y)
// -------------------------------------------------
// list contract
// list() == empty()
// list(a,b,c) == cons(a, cons(b, cons(c, empty())))
const __EMPTY__ = Symbol()
const empty = () =>
__EMPTY__
const list = (...xs) =>
xs.reduceRight((acc,x) => cons(x,acc), empty())
// -------------------------------------------------
// demo
const sum = xs =>
xs === empty()
? 0
: head(xs) + sum(tail(xs))
console.log(sum(list(1,2,3))) // 6
如何在第一个示例中返回一个数组,但在第二个示例中返回一个lambda(Function) - 这个细节并不重要,因为cons
的用户如果使用相应的cons
和head
访问者,仍然可以保证正确的数据。
如果合同仍然有效,我们可以根据需要多次更改实施细节。我们甚至可以像上一个例子中的tail
那样引入新的数据/代码。用户仍然只能使用__EMPTY__
和list
来保证正确的行为。
关于数据抽象的更多答案