FPOOP 是目前主流的编程范式.我们不谈论这两种编程范式的优劣, 仅仅讨论一下 FP OOP 两者的差别.

## Decomposition

• FP 中, 我们通常将程序分解为有着不同操作的函数
• OOP 中, 我们将程序分解为具有不同行为的类.

## Example

• 不同的表达式, 整数, 求反, 求和.
• 不用的表达式操作, eval, toString, hasZero.

--------------------------------------
|        | eval | toString | hasZero |
--------------------------------------
|   Int  |      |          |         |
--------------------------------------
|   Add  |      |          |         |
--------------------------------------
| Negate |      |          |         |
--------------------------------------


## Functional Approach

• 为表达式定义数据类型datatype, 为每一个类型增加构造函数(静态类型).
• 为每一个操作定义函数function.
• 在每一个函数中, 所有的数据类型都应该有一个分支branch.

### ML Code

exception BadResult of string

datatype exp =
Int    of int
| Negate of exp
| Add    of exp * exp
fun eval e =
case e of
Int _ =>e
| Negate e1 => (case eval e1 of
Int i => Int (~i)
| _ => raise BadResult "non-int in negation")
| Add(e1,e2) => (case (eval e1, eval e2) of
(Int i, Int j) => Int (i+j)
fun toString e =
case e of
Int i      => Int.toString i
| Negate e1  => "-(" ^ (toString e1) ^ ")"
| Add(e1,e2) => "("  ^ (toString e1) ^ " + " ^ (toString e2) ^ ")"
fun hasZero e =
case e of
Int i      => i=0
| Negate e1  => hasZero e1
| Add(e1,e2) => (hasZero e1) orelse (hasZero e2)


### Racket Code

#lang racket

(struct Int (e) #:transparent)
(struct Negate (e) #:transparent)

(define (eval e)
(cond ((Int? e) e)
((Negate? e) (Int (- (eval e))))

(define (toString e)
(cond ((Int? e) (number->string (Int-e e)))
((Negate? e) (string-append "-" (string-append "(" (string-append (toString (Negate-e e)) ")"))))
(string-append " + "

(define (hasZero e)
(cond ((Int? e) (= (Int-e e) 0))
((Negate? e) (hasZero (Negate-e e)))


## Object-Oriented Approach

• 为表达式定义类class, 为所有表达式定义一个抽象类, 可以为所有的子类添加公有的属性和方法.
• 为每一种数据类型定义一个子类subclass.
• 在每一个子类中, 为所有的操作定义方法method.

## Ruby Code

class Exp
# could put default implementations or helper methods here
end

class Int < Exp
def initialize i
@i = i
end
def eval
self
end
def toString
@i.to_s
end
def hasZero
i==0
end
end

class Negate < Exp
def initialize e
@e = e
end
def eval
Int.new(-e.eval.i) # error if e.eval has no i method (not an Int)
end
def toString
"-(" + e.toString + ")"
end
def hasZero
e.hasZero
end
end

def initialize(e1,e2)
@e1 = e1
@e2 = e2
end
def eval
Int.new(e1.eval.i + e2.eval.i) # error if e1.eval or e2.eval have no i method
end
def toString
"(" + e1.toString + " + " + e2.toString + ")"
end
def hasZero
e1.hasZero || e2.hasZero
end
end


## Extending Operations Or Variants

fun noNegConstants e =
case e of
Int i       => if i < 0 then Negate (Int(~i)) else e
| Negate e1   => Negate(noNegConstants e1)

(define (noNegConstants e)
(cond ((Int? e) (let ((i (Int-e e)))
(if (< i 0)
(Negate (Int (- i)))
(e))))
((Negate? e) (Negate (noNegConstants (Negate-e e))))


class Mult < Exp
def initialize(e1,e2)
@e1 = e1
@e2 = e2
end
def eval
Int.new(e1.eval.i * e2.eval.i) # error if e1.eval or e2.eval has no i method
end
def toString
"(" + e1.toString + " * " + e2.toString + ")"
end
def hasZero
e1.hasZero || e2.hasZero
end
end


----------------------------------------
|              |     OOP    |    FP    |
----------------------------------------
|  Operations  |     ^_^    |   v_v    |
----------------------------------------
|   Variants   |     v_v    |   ^_^    |
----------------------------------------


## Extensibility

• 函数式分解易于添加新的操作operation.
• 面向对象式分解易于添加新的variant.

### 关于评论和留言

Follow: Draveness · GitHub