摘要:眾多面向對象的編程思想雖不盡一致,但是無論哪種面向對象編程語言都具有以下的共通功能。原型編程以類為中心的傳統面向對象編程,是以類為基礎生成新對象。而原型模式的面向對象編程語言沒有類這樣一個概念。
“什么是面向對象?”這個問題往往會問到剛畢業的新手or實習生上,也是往往作為一個技術面試的開頭題。在這里我們不去談如何答(fu)好(yan)問(guo)題(qu),僅談談我所理解的面向對象。
從歷史上看,從20世紀60年代末期到70年代,分別有幾個不同領域都發展了面向對象的思想。比如數據抽象的研究、人工智能領域中的知識表現(框架模型)、仿真對象的管理方法(Simula)、并行計算模型(Actor)以及在結構化編程思想影響下而產生的面向對象方法。
框架模型是現實世界的模型化。從這個角度來看,“對象是對現實世界中具體事物的反映”這個觀點并沒有錯。
但是不管過去怎樣,現在對面向對象最好的理解是,面向對象編程是結構化編程的延伸。
結構化編程基本上實現了控制流程的結構化。但是程序流程雖然結構化了,要處理的數據卻并沒有被結構化。面向對象的設計方法是在結構化編程對控制流程實現了結構化后,又加上了對數據的結構化。
眾多面向對象的編程思想雖不盡一致,但是無論哪種面向對象編程語言都具有以下的共通功能。
1 . 不需要知道內部的詳細處理就可以進行操作(封裝、數據抽象)。
2 . 根據不同的數據類型自動選擇適當的方法(多態性)。
最早的時候是面向過程。想象一下一堆C語言or匯編堆砌在一起的函數互相調(shang)用(hai)的場景————什么?你說你沒學過C語言?那么你就想象一下一個復雜的SQL語句吧,有點像。
評論中有人提到了C語言并非完全不支持面向對象,Struct就是一個不錯的選擇。的確,但是C語言對面向對象的支持并不是那么的好。在絕大多數語言中都為Class(C++同時支持Struct和Class),但也有小部分語言沿用了這個經典的名字——比如Go語言。在這里特別說明是為了防止誤導新手把大象裝進冰箱需要幾步?
我們以“把大象裝進冰箱需要幾步”這個經典的腦經急轉彎來舉個例子吧:
面向過程打開冰箱,裝入大象,關上冰箱。這三步就是面向過程的思考方式,這種思想強調的是過程,也可以叫做動作。
open(icebox); putIn(icebox,elephant); close(icebox);面向對象
冰箱打開,冰箱存儲,冰箱關閉。這就是面向對象的思考方式,這種方式強調是對象,也可以說是實例。
//我們有一個冰箱 Icebox iceBox = new iceBox(); //可不能忘記大象,就叫它jake吧 Elephant jake = new Elephant(); icebox.open(); icebox.save(jake); icebox.close();什么是面向對象?
一種編程范式,相對于面向過程。為了方便在編程中更接近地去描述現實世界中的萬物(萬物皆對象),我們將對一個事物的描述稱之為類,而對象則是該事物的實例。
而面向對象的編程方法常見的有三種:
類模板方法
委派面向原型
組合
類模板在類中,我們把事物的屬性轉變為編程中的變量,把事物的行為轉變為方法。
Class Elephant{ public String name; public int age; public double weight; //更多的屬性...... //在這里的方法為了方便演示都是void public void eat(Food food){ //吃東西 } //更多的行為....... }對象
//我們再次召喚了jake Elephant jake = new Elephant(); //他隨便吃了點什么 jake.eat(new Something);面向對象所提供的特性 繼承
可以使子類復用父類公開的變量、方法
//幾百年后,jake和它的子孫們進化成了更強的大象 //它們被稱為:飛象 Class FlyElephant extends Elephant{ public void fly(){ //i belive i can fly~~ } } //其中有一頭飛象叫jason FlyElephant jason = new FlyElephant(); //不要問大象為什么能飛! jason.fly(); //而且還可以像其他大象一樣正常的吃東西 jason.eat(new Something);
如果把類當作模塊,繼承就是利用模塊的方法。繼承的思想好像有其現實的知識基礎,但是把它看做純粹的模塊利用方法則更恰當。封裝
因為繼承只不過是抽象的功能利用方法,所以不必把對繼承的理解束縛在“繼承是對現實事物的分類的反映”。實際上這樣的想法反而妨礙了我們對繼承的理解。
屏蔽一系列的細節。使外部調用時只要知道這個方法的存在
jason在eat的時候它或許先會分泌一點激素有助于它進食,然而我們在調用的時候并不知道發生了什么。
多態父類的方法繼承的到子類以后可以有不同的實現方式
jason在eat的時候它或許先會分泌一點激素有助于它進食,而jack在eat的時候或許會先刷個牙齒。
原型編程以類為中心的傳統面向對象編程,是以類為基礎生成新對象。類和對象的關系可以類比成鑄模和鑄件的關系。
而原型模式的面向對象編程語言沒有類這樣一個概念。
以JavaScript為例。需要生成新的對象時,只要給對象追加屬性。設置函數對象作為屬性的話,就成為方法。當訪問對象中不存在的屬性時,JavaScript 會去搜索該對象 prototype 屬性所指向的對象。
JavaScript 利用這個功能,使用“委派”而非“繼承”來實現面向對象編程。
// 生成Doge。...(1) function Doge(){ this.sit = function () {return "I"m the king of the world"} } // 從Doge 生成對象dog...(2) var doge = new Doge() // doge 是狗,所以能 sit...(3) alert(doge.sit()) // 生成新型myDoge...(4) function MyDoge () {} // 指定委派原型 MyDoge.prototype = new Dog() // 從MyDoge 生成新對象myDoge...(5) var myDoge = new MyDoge() document.write(myDoge.sit())
函數其實做到了對象構造器的作用
從原型生成對象:
生成對象;
將委派原型的內部屬性(__proto__)設置為 Dog.prototype;
調用函數 Dog,參數即為傳遞給 new 時的參數;
返回新生成的對象。
調用方法
定義原型函數,是空的
類似與第2步,生成新對象
和之前的Java通過類模板來實現面向對象的編程方式相比,原型對象系統支持一個更為直接的對象創建方法。例如,在 JavaScript 中,一個對象是一個簡單的屬性列表。每個對象包含另一個父類或原型 的一個特別引用,對象從父類或原型中繼承行為。
傳統對象系統和原型對象系統有本質的區別。傳統對象被抽象地定義為概念組的一部分,從對象的其他類或組中繼承一些特性。相反,原型對象被具體地定義為特定對象,從其他特定對象中繼承行為。
因此,基于類的面向對象語言具有雙重特性,至少需要 2 個基礎結構:類和對象。由于這種雙重性,隨著基于類的軟件的發展,復雜的類層次結構繼承也將逐漸開發出來。通常無法預測出未來類需要使用的方法,因此,類層次結構需要不斷重構,讓更改變得更輕松。
基于原型的語言會減少上述雙重性需求,促進對象的直接創建和操作。如果沒有通過類來束縛對象,則會創建更為松散的類系統,這有助于維護模塊性并減少重構需求。
然而話雖這么講,一大串原型鏈還是會讓人頭痛不已的,特別還是在動態語言中。
組合繼承(inheritance)是實現代碼重用的有力手段,但它并非永遠是完成這項任務的最佳工作。使用不當會導致軟件變得很脆弱。在包的內部使用繼承是非常安全的,在那里,子類和超類的實現都處于同一個程序員的控制下。對于專門為了繼承而設計的并且具有很好的文檔說明的類來說,使用繼承也是非常安全的。然而,對于普通的具體類進行跨超包邊界的繼承則是非常危險的。本條目并不適用于接口繼承(一個類實現一個接口,或者一個接口擴展另一個接口)。
方法調用不同的是,繼承打破了封裝性。子類信賴于其超類中特定功能的實現細節。超類的實現有可能會隨著發行版本的不同而有變化,子類有可能會被破壞。
在Java中,我們總是推薦使用interface而不是abstract class,這樣可以使代碼更加的靈活。在Java8后interface也是得到了增強——可以提供默認的方法實現。
另外,Go語言也是將組合發揮到極致的語言。
面向對象的好處接近人的思維,符合人類對現實世界的認知;
封裝特性可以使開發者不必在意內部的具體實現,更方便互相協作;
繼承特性可以減少代碼冗余,實現代碼復用;
多態特性令子類相比父類有不同的行為,這是非常接近現實的;
什么是面向對象一種相對于面向過程的編程范式。
Java程序員應了解的10個面向對象設計原則-原文面向對象設計原則是 OOPS(Object-Oriented Programming System,面向對象的程序設計系統)編程的核心,但大多數 Java 程序員追逐像 Singleton、Decorator、Observer 這樣的設計模式,而不重視面向對象的分析和設計。甚至還有經驗豐富的 Java 程序員沒有聽說過 OOPS 和 SOLID設計原則,他們根本不知道設計原則的好處,也不知道如何依照這些原則來進行編程。
眾所周知,Java 編程最基本的原則就是要追求高內聚和低耦合的解決方案和代碼模塊設計。查看 Apache 和 Sun 的開放源代碼能幫助你發現其他 Java 設計原則在這些代碼中的實際運用。Java Development Kit 則遵循以下模式:BorderFactory 類中的工廠模式、Runtime 類中的單件模式。你可以通過 Joshua Bloch 的《Effective Java》一書來了解更多信息。我個人偏向的另一種面向對象的設計模式是 Kathy Sierra 的 《Head First設計模式》 以及 《Head First Object Oriented Analysis and Design》。
雖然實際案例是學習設計原則或模式的最佳途徑,但通過本文的介紹,沒有接觸過這些原則或還在學習階段的 Java 程序員也能夠了解這 10 個面向對象的設計原則。其實每條原則都需要大量的篇幅才能講清楚,但我會盡力做到言簡意賅。
原則1:DRY(Don’t repeat yourself)即不要寫重復的代碼,而是用“abstraction”類來抽象公有的東西。如果你需要多次用到一個硬編碼值,那么可以設為公共常量;如果你要 在兩個以上的地方使用一個代碼塊,那么可以將它設為一個獨立的方法。SOLID 設計原則的優點是易于維護,但要注意,不要濫用,duplicate 不是針對代碼,而是針對功能。這意味著,即使用公共代碼來驗證 OrderID 和 SSN,二者也不會是相同的。使用公共代碼來實現兩個不同的功能,其實就是近似地把這兩個功能永遠捆綁到了一起,如果 OrderID 改變了其格式,SSN 驗證代碼也會中斷。因此要慎用這種組合,不要隨意捆綁類似但不相關的功能。
原則2:封裝變化在軟件領域中唯一不變的就是“Change”,因此封裝你認為或猜測未來將發生變化的代碼。OOPS 設計模式的優點在于易于測試和維護封轉的代碼。如果你使用 Java 編碼,可以默認私有化變量和方法,并逐步增加訪問權限,比如從 private 到 protected 和 not public。有幾種 Java 設計模式也使用封裝,比如 Factory 設計模式是封裝“對象創建”,其靈活性使得之后引進新代碼不會對現有的代碼造成影響。
原則3:開閉原則即對擴展開放,對修改關閉。這是另一種非常棒的設計原則,可以防止其他人更改已經測試好的代碼。理論上,可以在不修改原有的模塊的基礎上,擴展功能。這也是開閉原則的宗旨。
原則4:單一職責原則類被修改的幾率很大,因此應該專注于單一的功能。如果你把多個功能放在同一個類中,功能之間就形成了關聯,改變其中一個功能,有可能中止另一個功能,這時就需要新一輪的測試來避免可能出現的問題。
原則5:依賴注入或倒置原則這個設計原則的亮點在于任何被 DI 框架注入的類很容易用 mock 對象進行測試和維護,因為對象創建代碼集中在框架中,客戶端代碼也不混亂。有很多方式可以實現依賴倒置,比如像 AspectJ 等的 AOP(Aspect Oriented programming)框架使用的字節碼技術,或 Spring 框架使用的代理等。
原則6:優先利用組合而非繼承如果可能的話,優先利用組合而不是繼承。一些人可能會質疑,但我發現,組合比繼承靈活得多。組合允許在運行期間通過設置類的屬性來改變類的行為,也可以通過使用接口來組合一個類,它提供了更高的靈活性,并可以隨時實現。《Effective Java》也推薦此原則。
原則7:里氏代換原則(LSP)根據該原則,子類必須能夠替換掉它們的基類,也就是說使用基類的方法或函數能夠順利地引用子類對象。LSP 原則與單一職責原則和接口分離原則密切相關,如果一個類比子類具備更多功能,很有可能某些功能會失效,這就違反了 LSP 原則。為了遵循該設計原則,派生類或子類必須增強功能。
原則8:接口分離原則采用多個與特定客戶類有關的接口比采用一個通用的涵蓋多個業務方法的接口要好。設計接口很棘手,因為一旦釋放接口,你就無法在不中斷執行的情況 下改變它。在 Java 中,該原則的另一個優勢在于,在任何類使用接口之前,接口不利于實現所有的方法,所以單一的功能意味著更少的實現方法。
原則9:針對接口編程,而不是針對實現編程該原則可以使代碼更加靈活,以便可以在任何接口實現中使用。因此,在 Java 中最好使用變量接口類型、方法返回類型、方法參數類型等。《Effective Java》 和《Head First Design Pattern》書中也有提到。
原則 10:委托原則該原則最典型的例子是 Java 中的 equals () 和 hashCode () 方法。為了平等地比較兩個對象,我們用類本身而不是客戶端類來做比較。這個設計原則的好處是沒有重復的代碼,而且很容易對其進行修改。
總之,希望這些面向對象的設計原則能幫助你寫出更靈活更好的代碼。理論是第一步,更重要的是需要開發者在實踐中去運用和體會。
擴展閱讀學習面向對象的令狐沖
原型繼承-廖雪峰的官方網站
JavaScript--面向對象與原型(15)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/69788.html
摘要:當初看這個解釋有點懵逼,理解成閉包就是函數中的函數了。里的閉包最近不滿足于只干前端的活,開始用起了。里的閉包最近在學習語言,讓我們來看一下語言里的閉包。在中,閉包特指將函數作為值返回的情況,被返回的函數引用了生成它的母函數中的變量。 本人開始接觸編程是從js開始的,當時網上很多人說閉包是難點,各種地方對閉包的解釋也是千奇百怪。如今開始接觸js以外的各種編程語言,發現不光是js,php、...
摘要:當初看這個解釋有點懵逼,理解成閉包就是函數中的函數了。里的閉包最近不滿足于只干前端的活,開始用起了。里的閉包最近在學習語言,讓我們來看一下語言里的閉包。在中,閉包特指將函數作為值返回的情況,被返回的函數引用了生成它的母函數中的變量。 本人開始接觸編程是從js開始的,當時網上很多人說閉包是難點,各種地方對閉包的解釋也是千奇百怪。如今開始接觸js以外的各種編程語言,發現不光是js,php、...
摘要:很多情況下,通常一個人類,即創建了一個具體的對象。對象就是數據,對象本身不包含方法。類是相似對象的描述,稱為類的定義,是該類對象的藍圖或原型。在中,對象通過對類的實體化形成的對象。一類的對象抽取出來。注意中,對象一定是通過類的實例化來的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
摘要:很多情況下,通常一個人類,即創建了一個具體的對象。對象就是數據,對象本身不包含方法。類是相似對象的描述,稱為類的定義,是該類對象的藍圖或原型。在中,對象通過對類的實體化形成的對象。一類的對象抽取出來。注意中,對象一定是通過類的實例化來的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
摘要:很多情況下,通常一個人類,即創建了一個具體的對象。對象就是數據,對象本身不包含方法。類是相似對象的描述,稱為類的定義,是該類對象的藍圖或原型。在中,對象通過對類的實體化形成的對象。一類的對象抽取出來。注意中,對象一定是通過類的實例化來的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
閱讀 3433·2023-04-25 18:14
閱讀 1526·2021-11-24 09:38
閱讀 3244·2021-09-22 14:59
閱讀 3060·2021-08-09 13:43
閱讀 2562·2019-08-30 15:54
閱讀 562·2019-08-30 13:06
閱讀 1540·2019-08-30 12:52
閱讀 2719·2019-08-30 11:13