返回首页
当前位置: 主页 > 网络编程 > Ajax实例教程 >

FP and OOP编程范式介绍

时间:2017-12-04 21:57来源:知行网www.zhixing123.cn 编辑:麦田守望者

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

##Decomposition

如何将一个任务分解, 这是任何一位Programmer都需要考虑的事情, 而FPOOP对于如何将一个大型程序或者任务分解有着完全不同的方法.

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

##Example

我们通过举一个简单的例子, 来说明FPOOP之间的区别. 假设我们有:

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

这个问题就像实现下面二维表格中的每一个空格

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

无论你用的是什么编程语言, 都需要考虑如何实现表格中的每一个entry

##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)
                         | _ => raise BadResult "non-ints in addition")
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)
(struct Add (e1 e2) #:transparent)

(define (eval e)
  (cond ((Int? e) e)
        ((Negate? e) (Int (- (eval e))))
        ((Add? e) (Int (+ (eval (Add-e1 e)) (eval (Add-e2 e)))))
        (else (error "BadResult"))))

(define (toString e)
  (cond ((Int? e) (number->string (Int-e e)))
        ((Negate? e) (string-append "-" (string-append "(" (string-append (toString (Negate-e e)) ")"))))
        ((Add? e) (string-append "(" 
                                 (string-append (toString (Add-e1 e)) 
                                                (string-append " + " 
                                                               (string-append (toString (Add-e2 e)) ")")))))
        (else (error "BadResult"))))

(define (hasZero e)
  (cond ((Int? e) (= (Int-e e) 0))
        ((Negate? e) (hasZero (Negate-e e)))
        ((Add? e) (or (hasZero (Add-e1 e)) (hasZero (Add-e2 e))))))

##Object-Oriented Approach

在面向对象语言中, 将问题分解到类, 每一个类对应一种数据类型.

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

##Ruby Code

在静态语言中, 需要定义一个superclass声明所有需要实现的方法, 而在Ruby这种优雅的动态语言中, 我们写出superclass仅仅是为了反映逻辑.

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


class Int < Exp
  attr_reader :i
  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
  attr_reader :e
  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

class Add < Exp
  attr_reader :e1, :e2
  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

##Compare

我们可以看到, 函数是编程将程序分解为执行不同操作的函数, 而面向对象编程将其分解为拥有不同行为的类. 仅它们虽然是相反的, 但是确实相似的, 它们决定程序通过行分类或者列分类. 理解这个问题是非常极其重要的. 那么, 哪一种编程范式更好呢.

对于每个人来说可能有不同的答案, 对于我个人来说, 我更偏爱FP, 仅仅是因为它有意思, 当然pure functional programming或者pure object-oriented programming都是不完善的.

我们需要比较这两种范式在做不同方向的扩展的时候, 代码的变化.

##Extending Operations Or Variants

当我们使用Functional Porgramming的实现的时候, 添加一个操作是简单的. 我们可以直接增加一个function而不必更改现有的代码. 比如说, 我们增加一个函数将所有负数转化为正数.

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

      
(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))))
        ((Add? e) (Add (noNegConstants (Add-e1 e)) (noNegConstants (Add-e2 e))))
        (else (error "BadResult"))))

当遇到另一种情况, 添加数据类型的时候, 我们需要修改所有的function增加一个新的分支, 处理新的数据类型.

但是Object-oriented Programming的实现就能够很好的处理这种情况. 添加一个新的数据类型是非常简单的, 只需要实现一个子类, 不需要修改任何代码.

class Mult < Exp
  attr_reader :e1, :e2
  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.

当你提前计划未来会为你的系统添加operation或者variant时, 你可以提前选择使用哪种编程范式, 然而未来是难以预测的, 我们不太可能知道什么哪种拓展是更优秀的. 而且更可能的是, 行或者列的添加往往是都有的. 这就给我们的程序的拓展增加的了更多的困难.

实际上, 编程语言总会提供方法来降低Extensibility, 因为Extensibility is a double-edged sword. 正因为这些原因, 如何才能使软件兼具健壮性与拓展性是有价值但是困难的.

顶一下
(1)
100%
踩一下
(0)
0%
标签(Tag):编程范式
------分隔线----------------------------
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
验证码:点击我更换图片
猜你感兴趣