在函数式编程中重构比在OOP中更难吗?

时间:2017-06-17 09:30:17

标签: oop functional-programming refactoring

由于在OOP中您可以将许多细节封装(隐藏)为类中的私有字段,因此您可以隐藏大部分细节。因此,当你想要改变某些东西(重构)时,它通常是#34;更容易,因为在大多数情况下,变更的范围将是有限的。

另一方面,在函数式编程中,如果要更改某些内容(添加字段或更改函数输入/输出),则必须在整个软件中查找该元素的每次出现并更新它们(有时)在软件框架的情况下,用户在当前代码库之外),这可能是不可能的,并导致向后不兼容的更改。

1 个答案:

答案 0 :(得分:3)

起点

我们将从一个依赖于两个独立合同的小程序开始 - 配对和列表。只要合同履行

,这些合同的实施几乎可以是任何事情

例如,配对合同提供consheadtail - 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




第一个重构:配对

现在我要通过重新实现consheadtail来重构配对代码 - 请注意我们没有触及列表代码{{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用户如果使用相应的conshead访问者,仍然可以保证正确的数据。

如果合同仍然有效,我们可以根据需要多次更改实施细节。我们甚至可以像上一个例子中的tail那样引入新的数据/代码。用户仍然只能使用__EMPTY__list来保证正确的行为。

关于数据抽象的更多答案