托馬斯.庫爾提出「科學的革命」的範式論後,Robert Floyd在1979年圖靈獎的頒獎演說中使用了編程範式一詞。編程範式通常包括三個方面,以OOP爲例:html
1,學科的邏輯體系——規則範式:如 類/對象、繼承、動態綁定、方法改寫、對象替換等等機制。git
2,心理認知因素——心理範式:按照面向對象編程之父Alan Kay的觀點,「計算就是模擬」。OO範式極其重視隱喻(metaphor)的價值,經過擬人化,按照天然的方式模擬天然。程序員
3,天然觀/世界觀——觀念範式:強調程序的組織技術,視程序爲鬆散耦合的對象/類的組合,以繼承機制將類組織成一個層次結構,把程序運行視爲相互服務的對象之間的對話。github
簡單來講,編程範式是程序員看待程序應該具備的觀點,表明了程序設計者認爲程序應該如何被構建和執行的見解。web
常見的編程範式有:命令式、過程式、說明式、面向對象、函數式、泛型編程等。算法
馮諾依曼 機器語言、彙編語言 BASIC COBOL C Ada FORTRAN Fortran ,sql
腳本式 Perl Python PHP,把用其餘語言開發的獨立程序做爲部件「粘到一塊兒」數據庫
面向對象 Smalltalk C++ Java,將計算創建在獨立的對象的相互做用至上。每一個對象有其自身的內部狀態,以及管理自身狀態的可執行子程序express
函數式 Lisp ML Haskell ,程序被看做是一種從輸入到輸出的函數編程
數據流 ld Val,語言將計算當作在一些基本的功能結點之間流動的信息流。結點由輸入單詞的到達觸發,可以併發操做
邏輯式 Prolog,設法根據一集邏輯規則找出知足某些特定關係的值
基於模板的 XSLT xml html,
須要提醒的是:編程範式是編程語言的一種分類方式,它並不針對某種編程語言。就編程語言而言,一種語言能夠適用多種編程範式。
一些編程語言是專門爲某種特定範式設計的,例如C語言是過程式編程語言;Smalltalk和Java是較純粹的面向對象編程語言;Haskell是純粹的函數式編程語言。另一些編程語言和編程範式的關係並不一一對應,如Python,Scala,Groovy都支持面向對象和必定程度上的函數式編程。C++是多範式編程語言成功的典範。C++支持和C語言同樣的過程式編程範式,同時也支持面向對象編程範式,STL(Standard Template Library)使C++具備了泛型編程能力。支持多種範式多是C++直到如今仍然具備強大的生命力的緣由之一。
Swift是一門典型的多範式編程語言,即支持面向對象編程範式,也支持函數式編程範式,同時還支持泛型編程。Swift支持多種編程範式是由其創造目標決定的。Swift創造的初衷就是提供一門實用的工業語言。不一樣於Haskell這類出自大學和研究機構的學術性質的編程語言。蘋果推出Swift時就帶着着明確的商業目的:Mac OS和iOS系統的主要編程語言Objective-C已顯老態,Swift將使得蘋果系統的開發者擁有一門更現代的編程語言,從而促進蘋果整個生態圈的良性發展。
命令式編程的主要思想是關注計算機執行的步驟,即一步一步告訴計算機先作什麼再作什麼。
從本質上講,它是「馮.諾依曼機」運行機制的抽象,它的編程思想方式源於計算機指令的順序排列。
(也就是說:過程化語言模擬的是計算機機器的系統構造,而並非基於語言的使用者的我的能力和傾向。這一點咱們應該都很清楚,好比咱們最先曾經使用過的單片機的彙編語言。)
無論你用的是 C, C++ 仍是 C#, Java, Javascript, BASIC, Python, Ruby 等等,你均可以以這個方式寫。
程序流程圖是命令式語言進行程序編寫的有效輔助手段。
命令式語言特別適合解決線性(或者說循序漸進)的算法問題。它強調「自上而下(自頂向下)」「精益求精」的設計方式。這種方式很是相似咱們的工做和生活方式,由於咱們的平常活動都是循序漸進的順序進行的。
命令式語言趨向於開發運行較快且對系統資源利用率較高的程序。命令式語言很是的靈活並強大,同時有許多經典應用範例,這使得程序員能夠用它來解決多種問題。
命令式語言的不足之處就是它不適合某些種類問題的解決,例如那些非結構化的具備複雜算法的問題。問題出如今,命令式語言必須對一個算法加以詳盡的說明,而且其中還要包括執行這些指令或語句的順序。實際上,給那些非結構化的具備複雜算法的問題給出詳盡的算法是極其困難的。
普遍引發爭議和討論的地方是:無條件分支,或goto語句,它是大多數過程式編程語言的組成部分,反對者聲稱:goto語句可能被無限地濫用;它給程序設計提供了製造混 亂的機會。目前達成的共識是將它保留在大多數語言中,對於它所具備的危險性,應該經過程序設計的規定將其最小化。
命令式對實際事物處理通常能夠拆分爲如下兩種模式:
流程驅動:相似 通常就是主動輪詢 在幹活中還要分心 主動去找活幹 這樣有空餘的時間也徹底浪費掉了
採用警覺式者主動去輪詢 ( polling),行爲取決於自身的觀察判斷,是流程驅動的,符合常規的流程驅動式編程 ( Flow-Driven Programming)的模式。
事件驅動:相似 好比公司有一個oa系統 你幹完活的時候只須要看下oa系統有沒分配給你活 沒有能夠幹本身的事 不用擔憂還有其餘事沒幹完
採用託付式者被動等通知 (notification),行爲取決於外來的突發事件,是事件驅動 的,符合事件驅動式編程 ( Event-Driven Programming,簡稱 EDP)的模式。
其實,基於事件驅動的程序設計在圖形用戶界面(GUI)出現好久前就已經被應用於程序設計中,但是隻有當圖形用戶界面普遍流行時,它才逐漸形演變爲一種普遍使用的程序設計模式。
在過程式的程序設計中,代碼自己就給出了程序執行的順序,儘管執行順序可能會受到程序輸入數據的影響。
在事件驅動的程序設計中,程序中的許多部分可能在徹底不可預料的時刻被執行。每每這些程序的執行是由用戶與正在執行的程序的互動激發所致。
事件:就是通知某個特定的事情已經發生(事件發生具備隨機性)。
事件與輪詢:輪詢的行爲是不斷地觀察和判斷,是一種無休止的行爲方式。而事件是靜靜地等待事情的發生。事實上,在Windows出現以前,採用鼠標輸入字符模式的PC應用程序必須進行串行輪詢,並以這種方式來查詢和響應不一樣的用戶操作。
事件處理器:是對事件作出響應時所執行的一段程序代碼。事件處理器使得程序可以對於用戶的行爲作出反映。
事件驅動經常用於用戶與程序的交互,經過圖形用戶接口(鼠標、鍵盤、觸摸板)進行交互式的互動。固然,也能夠用於異常的處理和響應用戶自定義的事件等等。
事件的異常處理比用戶交互更復雜。
事件驅動不只僅侷限在GUI編程應用。可是實現事件驅動咱們還須要考慮更多的實際問題,如:事件定義、事件觸發、事件轉化、事件合併、事件排隊、事件分派、事件處理、事件連帶等等。
其實,到目前爲止,咱們尚未找到有關純事件驅動編程的語言和相似的開發環境。全部關於事件驅動的資料都是基於GUI事件的。
屬於事件驅動的編程語言有:VB、C#、Java(Java Swing的GUI)等。它們所涉及的事件絕大多數都是GUI事件。
此種程化範式要求程序員用循序漸進的算法看待每一個問題。很顯然,並非每一個問題都適合這種過程化的思惟方式。這也就致使了其它程序設計範式出現,包括咱們如今介紹的面向對象的程序設計範式。
從編程的發展史來談面向對象的出現。當軟件還很是簡單的時候,咱們只須要面向過程編程:
定義函數
函數一 函數二 函數三 函數四
定義數據
數據一 數據二 數據三 數據四
最後各類函數,數據的操做。
當軟件發展起來後,咱們的軟件變得愈來愈大,代碼量愈來愈多,複雜度遠超Hello World的時候,咱們的編寫就有麻煩了:函數和數據會定義得很是多,面臨兩個問題。首先是命名衝突,英文單詞也就那麼幾個,可能寫着寫着取名時就沒合適的短詞用了,爲了不衝突,只能把函數名取得愈來愈長。而後是代碼重複,咱們能夠用函數裏面調用函數的方法,可是函數調函數(好比一個功能多個方法(函數),幾個功能混用方法)不便於維護。
面向對象程序設計(Object-oriented programming OOP)是種經過類、方法、對象和消息傳遞,來支持面向對象的程序設計範式。對象則指的是類的實例。它將對象做爲程序的基本單元,將程序和數據封裝其中,以提升軟件的重用性、靈活性和擴展性,對象裏的程序能夠訪問及常常修改對象相關連的數據。在面向對象程序編程裏,程序會被設計成彼此相關的對象。
面向對象程序設計能夠看做一種在程序中包含各類獨立而又互相調用的對象的思想,這與傳統的思想恰好相反:傳統的程序設計主張將程序看做一系列函數的集合,或者直接就是一系列對計算機下達的指令。面向對象程序設計中的每個對象都應該可以接受數據、處理數據並將數據傳達給其它對象,所以它們均可以被看做一個小型的「機器」,即對象。即把事情交給最適合的對象去作。
面向對象和麪向過程的區別最直觀的比喻就如:搖(狗尾巴)和 狗.搖尾巴()的區別。
封裝,面向對象程序設計隱藏了某一方法的具體執行步驟,取而代之的是經過消息傳遞機制傳送消息給它。通過深刻的思考,作出良好的抽象,給出「完整且最小」的接口,並使得內部細節能夠對外隱藏
繼承,在某種狀況下,一個類會有「子類」。子類比本來的類(稱爲父類)要更加具體化;
多態,指由繼承而產生的相關的不一樣的類,其對象對同一消息會作出不一樣的響應;
使用面向對象編程語言,易於構建軟件模型。由於,對象很相似乎很容易和現實世界上的全部事物和概念。
面向對象經過接口
類,類是類似對象的集合。物以類聚——就是說明。每一個對象都是其類中的一個實體。類中的對象能夠接受相同的消息。換句話說:類包含和描述了「具備共同特性(數據元素)和共同行爲(功能)」的一組對象。
接口,每一個對象都有接口。接口不是類,而是對符合接口需求的類所做的一套規範。接口說明類應該作什麼但不指定如何做的方法。一個類能夠有一個或多個接口。
方法,方法決定了某個對象究竟可以接受什麼樣的消息。面向對象的設計有時也會簡單地概括爲「將消息發送給對象」。
面向對象技術一方面借鑑了哲學、心理學、生物學的思考方式,另外一方面,它是創建在其餘編程技術之上的,是之前的編程思想的天然產物。
若是說結構化軟件設計是將函數式編程技術應用到命令式語言中進行程序設計,面向對象編程不過是將函數式模型應用到命令式程序中的另外一途徑,此時,模塊進步爲對象,過程龜縮到class的成員方法中。OOP的不少技術——抽象數據類型、信息隱藏、接口與實現分離、對象生成功能、消息傳遞機制等等,不少東西就是結構化軟件設計所擁有的、或者在其餘編程語言中單獨出現。但只有在面嚮對象語言中,他們才共同出現,以一種獨特的合做方式互相協做、互相補充。
從上面能夠看到,若是按照面向過程的方法去設計汽車,汽車廠商須要採購一大堆零件,而後研究如何調試、調用這一大堆零件以完成一個功能。可是若是採用面向對象的方法去設計汽車,那麼汽車廠商能夠採用外包的方式交給專業的制動系統廠商來設計,只須要約定須要開放哪些public方法,輸入什麼輸出什麼就能夠了。
將功能有聯繫的一批函數放在一塊兒封裝成一個類。這種類能夠徹底沒有內部數據,也能夠有數據。當有數據時,這些數據充當的其實就是配置(配置對於一個設計優秀的對象,是透明的,對象自己內部的函數根本不知道有配置這個東西,它只知道它須要的每個數據在它new以後就已經存在this裏了,隨取隨用。配置的給予或獲取方式,是構建對象(new)時才須要去考慮的)這種對象的特色是,它的每個函數(或方法)對這些數據都是隻讀的,因此無論方法有無被調用,被誰調用,被調用多少次,它也不會改變它的狀態。
這個概念是相對於傳統的面向數據庫的系統分析和設計而言的。數據庫雖然只用了外鍵就描述了複雜的大千世界,但軟件開發的難點在於適應變化,而且可以安全地修改。關係模型看似簡單,但它卻像一張蜘蛛網同樣將全部table和欄位包在一塊,牽一髮而動全身,讓你在修改時如履薄冰,一不當心就會顧此失彼,bug此起彼伏。而OO的封裝特性則恰好能夠用來解決這個問題。將業務數據整理成一個個獨立的對象,讓它們的數據只能被本身訪問。留給外界的基本上只是一些接口(方法),數據除非萬不得已,一個都不會公開。外界只能向它發送消息,它本身則經過修改自身數據來響應這種消息。這種對象與第一種對象恰好相反,它必定有數據,並且它的每個函數存在的目的就是修改本身的數據。且每一次修改都是粗粒度的,每一次修改後,對象也仍是處在valid狀態。推薦閱讀《領域模型淺析》,《領域模型,你真的理解的了嗎?》
順便拓展下:領域驅動設計(Domain-Driven Design)-貧血模型-領域模型-充血模型
其它用來解決過程式開發時,超多的變量,超複雜的流程而整理出來的小對象,。這些對象一塊兒協做,最後完成一個傳統成千上萬行的過程式代碼才能完成的功能。例如如今要鏈接sql server執行查詢語句並取得結果返回。不使用任何類庫和工具,全部步驟都本身進行,例如解析協議,socket網絡鏈接,數據包收發等。這時候從頭至尾用一個個函數來完成,絕對沒有先劃分出一個個職責分明的對象,讓各對象協做完成這件事情來得更簡單。
但編程實踐代表,並非任何東西成爲對象都是一件好事情。舉一個Java中的蹩足的例子:Java中只有對象才能做爲參數傳入函數(固然還有原始類型primitive type)。因此爲了將函數傳遞給另一個函數,你須要將函數包裹在一個對象中,一般會用一個匿名類,由於這個類不會有其餘做用,只是爲了讓Java的一切皆爲對象的設計高興。
Java擁有純粹的面向對象概念。它從設計之初,就但願以一切皆爲對象的純對象模型來爲世界建模。但發展到如今,Java中加入了愈來愈多非對象的東西。引入了閉包,從而得到了函數式編程中的一級函數;引入泛型,從而得到了參數化的類型。這可能暗示了,這個世界是如此得豐富多彩,使用單一模式爲世界建模並不會成功。
聲明式編程是以數據結構的形式來表達程序執行的邏輯。它的主要思想是告訴計算機應該作什麼,但不指定具體要怎麼作。
SQL 語句就是最明顯的一種聲明式編程的例子,例如:
SELECT * FROM collection WHERE num > 5
除了 SQL,網頁編程中用到的 HTML 和 CSS 也都屬於聲明式編程。
經過觀察聲明式編程的代碼咱們能夠發現它有一個特色是它不須要建立變量用來存儲數據。
另外一個特色是它不包含循環控制的代碼如 for, while。
函數式編程和聲明式編程是有所關聯的,由於他們思想是一致的:即只關注作什麼而不是怎麼作。但函數式編程不只僅侷限於聲明式編程。
函數式編程(functional programming)或稱函數程序設計、泛函編程,是一種編程範式,它將計算機運算視爲函數運算,而且避免使用程序狀態以及易變對象。其中,λ演算(lambda calculus)爲該語言最重要的基礎。並且,λ演算的函數能夠接受函數看成輸入(引數)和輸出(傳出值)。
函數式編程關心類型(代數結構)之間的關係,命令式編程關心解決問題的步驟。函數式編程中的lambda能夠當作是兩個類型之間的關係,一個輸入類型和一個輸出類型。lambda演算就是給lambda表達式一個輸入類型的值,則能夠獲得一個輸出類型的值,這是一個計算,計算過程知足 -等價和 -規約。函數式編程的思惟就是如何將這個關係組合起來,用數學的構造主義將其構造出你設計的程序
比起命令式編程,函數式編程更增強調程序執行的結果而非執行的過程,倡導利用若干簡單的執行單元讓計算結果不斷漸進,逐層推導複雜的運算,而不是設計一個複雜的執行過程。
命令式編程是面向計算機硬件的抽象,有變量(對應着存儲單元),賦值語句(獲取,存儲指令),表達式(內存引用和算術運算)和控制語句(跳轉指令),一句話,命令式程序就是一個馮諾依曼機的指令序列。
而函數式編程是面向數學的抽象,將計算描述爲一種表達式求值,一句話,函數式程序就是一個表達式。
函數式編程最重要的特色是「函數第一位」,即函數能夠出如今任何地方,好比你能夠把函數做爲參數傳遞給另外一個函數,不只如此你還能夠將函數做爲返回值。
函數式編程中的函數這個術語不是指計算機中的函數(其實是Subroutine),而是指數學中的函數,即自變量的映射。也就是說一個函數的值僅決定於函數參數的值,不依賴其餘狀態。好比sqrt(x)函數計算x的平方根,只要x不變,不論何時調用,調用幾回,值都是不變的。
在函數式語言中,函數做爲一等公民,能夠在任何地方定義,在函數內或函數外,能夠做爲函數的參數和返回值,能夠對函數進行組合。
純函數式編程語言中的變量也不是命令式編程語言中的變量,即存儲狀態的單元,而是代數中的變量,即一個值的名稱。變量的值是不可變的(immutable),也就是說不容許像命令式編程語言中那樣屢次給一個變量賦值。好比說在命令式編程語言咱們寫「x = x + 1」,這依賴可變狀態的事實,拿給程序員看說是對的,但拿給數學家看,卻被認爲這個等式爲假。
函數式語言的如條件語句,循環語句也不是命令式編程語言中的控制語句,而是函數的語法糖,好比在Scala語言中,if else不是語句而是三元運算符,是有返回值的。
嚴格意義上的函數式編程意味着不使用可變的變量,賦值,循環和其餘命令式控制結構進行編程。
從理論上說,函數式語言也不是經過馮諾伊曼體系結構的機器上運行的,而是經過λ演算來運行的,就是經過變量替換的方式進行,變量替換爲其值或表達式,函數也替換爲其表達式,並根據運算符進行計算。λ演算是圖靈徹底(Turing completeness)的,可是大多數狀況,函數式程序仍是被編譯成(馮諾依曼機的)機器語言的指令執行的。
函數是"一等公民":函數優先,和其餘數據類型同樣。
只用"表達式",不用"語句":經過表達式(expression)計算過程獲得一個返回值,而不是經過一個語句(statement)修改某一個狀態。
無反作用:不污染變量,同一個輸入永遠獲得同一個數據。
不可變性:前面一提到,不修改變量,返回一個新的值。
因爲變量值是不可變的,對於值的操做並非修改原來的值,而是修改新產生的值,原來的值保持不便。
一般來講,算法都有遞推(iterative)和遞歸(recursive)兩種定義。
因爲變量不可變,純函數編程語言沒法實現循環,這是由於For循環使用可變的狀態做爲計數器,而While循環或DoWhile循環須要可變的狀態做爲跳出循環的條件。所以在函數式語言裏就只能使用遞歸來解決迭代問題,這使得函數式編程嚴重依賴遞歸。
函數式語言固然還少不了如下特性:高階函數(Higher-order function):就是參數爲函數或返回值爲函數的函數。有了高階函數,就能夠將複用的粒度下降到函數級別,相對於面嚮對象語言,複用的粒度更低。
偏應用函數(Partially Applied Functions):一個函數接收一個有多個參數的函數,返回一個須要較少參數的函數。偏函數將一到多個參數在內部固定,而後返回新函數,返回的函數接收剩餘的參數完成函數的應用。
柯里化(Currying):輸入一個有多個參數的函數, 返回一個只接收單個參數的函數。
閉包(Closure):閉包就是有權訪問另外一個函數做用域中變量的函數.閉包的三個特性:1.閉包是定義在函數中的函數 。2.閉包能訪問包含函數的變量。3.即便包含函數執行完了, 被閉包引用的變量也得不到釋放。具體參看《閒話閉包》
因爲命令式編程語言也能夠經過相似函數指針的方式來實現高階函數,函數式的最主要的好處主要是不可變性帶來的。沒有可變的狀態,函數就是引用透明(Referential transparency)的和沒有反作用(No Side Effect)。
函數即不依賴外部的狀態也不修改外部的狀態,函數調用的結果不依賴調用的時間和位置,這樣寫的代碼容易進行推理,不容易出錯。這使得單元測試和調試都更容易。
因爲(多個線程之間)不共享狀態,不會形成資源爭用(Race condition),也就不須要用鎖來保護可變狀態,也就不會出現死鎖,這樣能夠更好地併發起來,尤爲是在對稱多處理器(SMP)架構下可以更好地利用多個處理器(核)提供的並行處理能力。
我以爲函數編程的好處就不用管js裏面該死的this指向
函數式編程語言還提供惰性求值-Lazy evaluation,也稱做call-by-need,是在將表達式賦值給變量(或稱做綁定)時並不計算表達式的值,而在變量第一次被使用時才進行計算。這樣就能夠經過避免沒必要要的求值提高性能。
函數式編程語言通常還提供強大的模式匹配(Pattern Match)功能。在函數式編程語言中能夠定義代數數據類型(Algebraic data type),經過組合已有的數據類型造成新的數據類型,如在Scala中提供case class,代數數據類型的值能夠經過模式匹配進行分析。
函數式編程天生親和單元測(特別是黑盒測試),由於FP關注就是輸入與輸出。反觀Java或者C++,僅僅檢查函數的返回值是不夠的:代碼可能修改外部狀態值,所以咱們還須要驗證這些外部的狀態值的正確性。在FP語言中呢,就徹底不須要。
調試查錯方面,由於FP程序中的錯誤不依賴於以前運行過的不相關的代碼。而在一個指令式程序中,一個bug可能有時能重現而有些時候又不能。由於這些函數的運行依賴於某些外部狀態, 而這些外部狀態又須要由某些與這個bug徹底不相關的代碼經過某個特別的執行流程才能修改。在FP中這種狀況徹底不存在:若是一個函數的返回值出錯了,它一直都會出錯,不管你以前運行了什麼代碼。而整個程序就是函數接龍。
推薦閱讀《傻瓜函數式編程
泛型編程是另一個有趣的話題。泛型爲程語言提供了更高層級的抽象,即參數化類型。換句話說,就是把一個本來特定於某個類型的算法或類當中的類型信息抽象出來。這個抽象出來的概念在C++的STL(Standard Template Library)中就是模版(Template)。STL展現了泛型編程的強大之處,一出現就成爲了C++的強大武器。除C++以外,C#,Java,Haskell等編程語言都引入了泛型概念。
泛型編程是一個稍微局部一些的概念,它僅僅涉及如何更抽象地處理類型,即參數化類型。這並不足以支撐起一門語言的核心概念。咱們不會聽到一個編程語言是純泛型編程的,而沒有其餘編程範式。但正由於泛型並不會改變程序語言的核心,因此在大多數時候,它能夠很好的融入到其餘的編程方式中。C++,Scala,Haskell這些風格迥異的編程語言都支持泛型。泛型編程提供了更高的抽象層次,這意味着更強的表達能力。這對大部分編程語言來講都是一道美味佐餐美酒。
在Swift中,泛型獲得普遍使用,許多Swift標準庫是經過泛型代碼構建出來的。例如Swift的數組和字典類型都是泛型集。這樣的例子在Swift中隨處可見。
參考文章:
編程範式:命令式編程(Imperative)、聲明式編程(Declarative)和函數式編程(Functional)
神奇的λ演算 https://www.cnblogs.com/dragonpig/archive/2010/01/26/1657052.html
編程語言範式 http://www.cnblogs.com/lisperl/archive/2011/11/20/2256165.html
λ 演算學習 http://www.javashuo.com/article/p-fttrfqid-hh.html
函數式編程漫談 https://cloud.tencent.com/developer/article/1190773
此文大可能是本文給出的連接文字提煉總結,若是不妥之處,請到本站留言,告知,拜謝!