不一樣維度的拆分:面向對象和函數式的區別

面向對象是什麼

咱們學習編程的時候,學完基礎的語法就是學面向對象了吧。對於面向對象每一個人都應該有一些理解,我這裏講一下個人理解。編程

世間萬事萬物皆爲對象,對象包括屬性和行爲。咱們只須要把咱們所關心的對象、屬性、行爲抽象出來就行了。好比兔子,若是咱們關心的是龜兔賽跑的過程,那麼咱們只須要抽象出他速度、耐力等相關屬性,他只須要跑和休息的方法,但若是咱們是作生物研究的話,可能要抽象出毛髮長度、耳朵形狀等等屬性,須要叫、跑、跳等方法。具體的封裝粒度和形式與所關心的過程有關。後端

不一樣的語言對於面向對象的實現不同,有的是類式的,如Java、C++,有的是原型式,好比JavaScript,這只是不一樣的實現方式。思想和抽象的過程仍是同樣的。數組

面向對象的擴展方式是組合和繼承,包括屬性和方法兩部分的複用。bash

函數式是什麼

有個權威的定義是: 程序 = 邏輯 + 數據,邏輯部分能夠拆分紅一個個的函數,劃分函數是爲了複用和邏輯清晰,就像劃分ui組件的目的同樣。而若是一個函數與上下文耦合了,或者內部有一個可變的狀態,那麼它是很難複用的,由於你要先把他須要的環境配齊了,你才能去用。而沒有內部狀態和對外部環境依賴的函數是複用性很高的,叫作純函數。純函數和不純的函數的區別就像綠色軟件和須要安裝的軟件的區別同樣,一個是依賴環境的,一個是對環境無依賴的。可能不那麼準確,但能夠直觀感覺下純函數的好處。函數

純函數由於對內部狀態和外部環境都沒有依賴,因此一個輸入值,對應着惟一的輸出值,因此也能夠把它當成一個變量。而變量是能夠進行算術、邏輯、比較等運算的,對應到純函數也就有了算術運算函數、邏輯運算函數、比較運算函數等。學習

純函數是不能有內部狀態、也不能依賴上下文的,但確實有一些數據是在上下文中,這時須要再包一層函數,叫作Monad,而應用具體函數到這個被包裹的值得函數叫作Functor。fetch

複用性很高的純函數,根據具體邏輯的需求進行組合,好比串行的調用(pipe、compose),來完成具體的過程,組合須要接口統一,就像機械零件同樣,因此統一成一個參數的比較好組合,函數參數歸一化叫作currify。經過組合一系列的單個函數,完成不一樣邏輯過程,這就叫函數式。數據最後傳入組合好的函數。ui

以下就是一個函數式的例子,經過組合把一系列過程封裝到一個函數內,而後把數據傳入這個函數就能完成整個過程。就像水流過管道同樣,更直觀點的感覺能夠說是先搭好了多米諾骨牌,而後把第一張骨牌推倒。這就是函數式的形式:組合好了函數管道,數據最後傳入。spa

// 提取 tasks 屬性
var SelectTasks = R.prop('tasks');

// 過濾出指定的用戶
var filterMember = member => R.filter(
  R.propEq('username', member)
);

// 排除已經完成的任務
var excludeCompletedTasks = R.reject(R.propEq('complete', true));

// 選取指定屬性
var selectFields = R.map(
  R.pick(['id', 'dueDate', 'title', 'priority'])
);

// 按照到期日期排序
var sortByDueDate = R.sortBy(R.prop('dueDate'));

// 合成函數
var getIncompleteTaskSummaries = function(membername) {
  return fetchData().then(
    R.pipe(
      SelectTasks,
      filterMember(membername),
      excludeCompletedTasks,
      selectFields,
      sortByDueDate,
    )
  );
};
複製代碼

面向對象和函數式的區別是什麼

面向對象是比較常見的思路,而函數式也是一種編程的思路,或者說這是兩種編程範式。這二者的關係其實咱們身邊也能找到對應的。code

好比目錄結構的劃分能夠有兩種維度,一種是先按代碼功能劃分再按業務模塊劃分:

components
   user-login
   goods-list
pages
    user-login
    goods-list
store
   user-login
    goods-list
utils
assets
複製代碼

一種是先按業務模塊劃分再按代碼功能劃分:

user-login
    components
    pages
    store
    assets
    utils
goods-list
    components
     pages
    store
    assets
    utils
複製代碼

這兩種方式哪一種更好呢,其實須要看具體狀況,若是業務模塊特別多,每一個模塊差異可能比較大,那麼第二種方式更好,若是業務模塊比較少,且基本都是同樣的,那麼第一種方式比較好。

這其實就和函數式與面向對象的區別同樣,程序 = 數據 + 邏輯, 面向對象就像第二種方式,把數據和邏輯封裝到了一塊兒,做爲總體來複用和組合,而函數式則是把數據和邏輯分開,邏輯部分經過函數的組合來複用,以後再傳入數據。

因此,函數式和麪向對象也就沒有哪一個更好一說,若是是數據和邏輯的關係耦合緊密,那麼仍是封裝成對象來複用更好,若是邏輯比較獨立,那麼邏輯部分用函數式來拆分和複用更好。這只是兩種劃分維度。

通常來講遊戲中用面向對象比較多,由於他們涉及到的對象都是數據和方法耦合特別緊密的,好比子彈,你若是用函數式的方式把子彈的數據和子彈運動的函數分開,也沒啥意義,一是由於子彈運動的函數對別的模塊來講沒有多大的複用和組合的價值,二是分開這兩部分可能會致使程序很難理解。後端的代碼也通常是面向對象比較多,可是通常後端的Model層都是貧血模型,就是操做數據的邏輯和數據實體類是分開封裝的,我以爲這樣的話用函數式可能會更好。函數式用的最多的領域仍是科學計算領域,由於這些計算過程是徹底的與數據無關的,也叫pointfree的。

總結

面向對象是以所研究的業務實體爲角度來抽象和封裝對應的屬性和方法,以實體的方式來組合和複用,組合方式有繼承、組合等。而函數式是另外一個維度的劃分,把數據和邏輯分開,對邏輯部分劃分紅容易複用的純函數,同時提供一系列的算術、邏輯、關係運算函數,以後經過函數組合來複用。

這兩種方式只是不一樣的劃分角度,就像目錄結構的劃分同樣。通常數據和邏輯耦合很高的業務過程會用面向對象,好比遊戲開發,而邏輯和數據關係不緊密的(pointfree的)會用函數式,好比科學計算。

相關文章
相關標籤/搜索