oop - 函数式编程中的重构是否比 OOP 中的重构更困难?

标签 oop functional-programming refactoring

由于在 OOP 中您可以将很多细节封装(隐藏)为类中的私有(private)字段,因此您可以隐藏大部分细节。因此,当您想要更改某些内容(重构)时,“通常”会更容易,因为大多数时候更改的范围是有限的。

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

最佳答案

起点

我们将从一个依赖于两个独立合约——Pair 和 List 的小程序开始。只要契约(Contract)得到履行,这些契约(Contract)的实现几乎可以是任何事情

例如,配对合约给出 cons , head , 和 tailhead(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 来重构 Pair 代码。 , headtail – 注意我们没有触及列表代码 emptylist ,以及演示代码 sum不需要改变

// -------------------------------------------------
// 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


第二次重构:列表

现在我要更改 List 实现,但仍要确保契约(Contract)已履行 - 请注意,我不必更改 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__ = 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



一个又一个...

我们实现的合约有效地封装了实现细节,就像 OO 程序中的私有(private)数据/方法一样。注意 cons在第一个示例中返回一个数组,但在第二个示例中返回一个 lambda(函数)——这个细节无关紧要,因为 cons 的用户如果对应的 head 仍然保证正确的数据和 tail使用访问器。

只要契约(Contract)仍然履行,我们可以根据需要多次更改实现细节。我们甚至可以像 __EMPTY__ 那样引入新的数据/代码。在最后一个例子中。用户仍然只能使用 listempty以保证正确的行为。

更多关于数据抽象的答案
  • Map array structure
  • Why do functional languages make such heavy use of lists
  • Take a list and return new list with count of positive, negative, and zeros
  • and more...
  • 关于oop - 函数式编程中的重构是否比 OOP 中的重构更困难?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44602945/

    相关文章:

    c# - ValidationRule 的执行集-C# 类设计 - 更好的方法

    php - 使用 PHP 在静态方法中访问 Class 属性的最佳方法

    android - 将开源的JniLibs整合到自己的android项目中

    Haskell: 'tell ["abc“]' evaluation throwing "非类型变量参数”错误

    haskell - 如何使用foldr/foldl定义foldM(如果可能的话)?

    refactoring - Pycharm重构-重命名内部函数

    python - 重构Django基于类的 View ,清理18个重复类。

    c# - Java和c#中 protected 成员的区别

    nhibernate - 建模一对零或一对关系(Z 基数)

    functional-programming - 在 Elm 中,如何迭代 map ?