深入理解面向对象,面向对象3个特性7个原则6种关系
為什么會(huì)有這么多的概念?知其所以然。
軟件,是為了解決人類發(fā)展的問題,方便人類的工具,如果一個(gè)計(jì)算機(jī),或者一個(gè)軟件只會(huì)算數(shù),那么遇見匯率換算怎么辦?是不是可以說計(jì)算機(jī)本身如果沒有太多的固有功能,就無法完成某些事。而現(xiàn)在的軟件,可以幫助人們購物,相親,娛樂。這么復(fù)雜的功能來說,如果軟件思想沒有發(fā)展出更高級(jí)的思想,它是無法為人類服務(wù)的,所以為了可維護(hù)性更強(qiáng),具有更強(qiáng)的功能,必須在計(jì)算機(jī)軟件編程本身上提出一些更高級(jí)的概念,用于實(shí)現(xiàn)比算加減乘除更復(fù)雜的功能,多樣化的規(guī)則成了一套實(shí)用高效的規(guī)范,那么就是下文看到的各種計(jì)算機(jī)名詞。
面向?qū)ο?/h2>
面向
“面向”此處可以理解為按照什么思路去編程。
對(duì)象
對(duì)象是一種依照事物為中心的編程思想,萬物皆對(duì)象,由實(shí)體引發(fā)事件,對(duì)象是真實(shí)存在的,對(duì)象也可以理解為是把數(shù)據(jù)結(jié)構(gòu)和處理它們的方法,組成對(duì)象。達(dá)到了軟件工程的三個(gè)目標(biāo),重用性靈活性和擴(kuò)展性。
重用性: 一個(gè)類里面的方法可以多次使用。
靈活性: 可以表現(xiàn)為多態(tài),可重復(fù)調(diào)動(dòng)等特點(diǎn),自由度很高,條條大路通羅馬。
擴(kuò)展性: 多態(tài),繼承都有這個(gè)特性,可便于多樣化擴(kuò)展,進(jìn)行抽離,降低耦合。
類
可以理解為對(duì)象的抽象化概念,分離出相同的特點(diǎn),并加以歸類。
把相同行為的對(duì)象歸納為類,一個(gè)人是一個(gè)對(duì)象,但是多個(gè)人可以歸納為一個(gè)人類,人類指的不是某一個(gè),而是一個(gè)虛擬的實(shí)體概念。為什么有人這個(gè)類?因?yàn)槿擞须p手,會(huì)用火,有文明,學(xué)習(xí)能力強(qiáng)等因素,如果想要從人類上說某個(gè)人,那就是舉一個(gè)實(shí)例,也就是實(shí)例化。
成員屬性
屬性可以理解數(shù)據(jù),數(shù)據(jù)是信息和表現(xiàn)形式的載體。依照人為例,人有鼻子有眼,這個(gè)就是人的屬性。可以用 有 來形容。
成員方法
方法可以理解為函數(shù)。方法是控制數(shù)據(jù)的管家。依照人為例,人會(huì)說話,會(huì)吃飯,這就人的方法。可以用 做 來形容。
接口
可以理解為一個(gè)類中需要指定它需要做什么,但是不需要去做,起到一個(gè)規(guī)范示例的作用,就是一個(gè)標(biāo)準(zhǔn),需要其它的類去實(shí)現(xiàn)它。例如定義了usb接口的尺寸,大小,數(shù)據(jù)線連接方法,可以類比成一個(gè)接口規(guī)范,全世界的usb接口都通用,無論是U盤,充電器線,鼠標(biāo),鍵盤。這些實(shí)例都可以根據(jù)這個(gè)約束規(guī)范,去制造東西。接口的使用需要用 實(shí)現(xiàn) 兩個(gè)字形容。
類的三個(gè)特性
封裝: 通過封裝隱藏類的內(nèi)部方法或者數(shù)據(jù),只允許訪問可以訪問的資源,保證安全性。擬人化來說,就是姓名是公開的,銀行密碼的信息是個(gè)人的,PHP可用public protected private去修飾。
繼承: 繼承使類得到泛化,兒子繼承爸爸,可以獲得父級(jí)或者父級(jí)往上的非私有屬性,或者方法,使用extends關(guān)鍵字。
多態(tài): 可實(shí)現(xiàn)基于對(duì)象類型的動(dòng)態(tài)分派,不同的人做同一件事,得到不同的結(jié)果。相親這件事:女生遇到流氓會(huì)說滾,遇到帥哥會(huì)說么么噠。
面向?qū)ο髨鼍跋戮幊?個(gè)原則,宗旨:面向接口編程,針對(duì)目標(biāo)。精簡代碼,降低耦合。靈活分離,減少影響。抽離相同的代碼,便于復(fù)用維護(hù)。
單一原則: 一個(gè)類就做一件事,職責(zé)被完整的封裝在一個(gè)類中。不會(huì)引起混亂,提高重用性,低耦合的設(shè)計(jì)可以降低軟件開發(fā)后期的維護(hù)。
開閉原則: 對(duì)修改關(guān)閉,對(duì)擴(kuò)展開放。因?yàn)樾枨笞兏?#xff0c;運(yùn)行環(huán)境升級(jí)等原因需要改代碼,如果沒有出現(xiàn)bug,那么不推薦改原來的代碼,可以在原基礎(chǔ)上進(jìn)行擴(kuò)充。既增加了擴(kuò)展性,又保證原來的邏輯不出問題。所謂的“祖?zhèn)鞔a,勿動(dòng)”,也就是這么一回事。
里氏代換原則: 通俗講:所有能用到老爸的地方,兒子也能使用。軟件中能夠使用的基類對(duì)象,那么就要做到任何地方能使用子類對(duì)象而不受影響。也就是子類能夠替換程序中父類出現(xiàn)的任何地方,并保證邏輯不變和正確,這里面暗含著不推薦重寫父類的意思。正是因?yàn)橛辛诉@個(gè)標(biāo)準(zhǔn),才能防止父級(jí)一旦修改,就會(huì)殃及子級(jí)發(fā)生故障的情況。這個(gè)原則實(shí)現(xiàn)了開閉原則的形式,子類就相當(dāng)于擴(kuò)展。實(shí)質(zhì)上這個(gè)原則是要告訴我們,繼承需要注意的問題和遵循的原則。但繼承是增加了父子類的耦合關(guān)系,為了解決依賴,可以適當(dāng)通過聚合,組合,依賴等來解決。
依賴倒轉(zhuǎn)原則: 就是面向接口編程。把共用的可復(fù)用的方法放置到抽象類,抽象思維編程,或者接口當(dāng)中,依照抽象耦合的方式是原則的關(guān)鍵,目的是降低耦合。高層模塊(舉個(gè)例子:高層模塊就是框架底層的代碼,依照PHP為例,框架底層的代碼,好多都是抽象類或者接口)不依賴底層模塊,二者依賴于抽象。抽象不應(yīng)該依賴細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴抽象,也就是做到細(xì)節(jié)和非細(xì)節(jié)的分離,相對(duì)于細(xì)節(jié)的多變性,抽象的東西要穩(wěn)定的多。抽象為基礎(chǔ)搭建的架構(gòu)比細(xì)節(jié)為基礎(chǔ)的架構(gòu)要穩(wěn)定的多。舉個(gè)例子,功能的大體實(shí)現(xiàn)可以使用抽象類,但是細(xì)節(jié),可以使用具體的類去實(shí)現(xiàn)。這里所謂的抽象,是用于定制規(guī)范和整體架構(gòu)。
舉個(gè)栗子:
再看看 優(yōu)化的結(jié)果,所謂的抽象就是發(fā)送信息,細(xì)節(jié)就是發(fā)送信息的方式和內(nèi)容,抽象和細(xì)節(jié)做了分離,降低耦合。
<?php//接口定義大致的方法,大方向 interface SendMsg {public function send(); }//具體的類去實(shí)現(xiàn)細(xì)節(jié) class Email implements SendMsg {public function send() {return '發(fā)送電子郵件';} }//具體的類去實(shí)現(xiàn)細(xì)節(jié) class WeChat implements SendMsg {public function send() {return '發(fā)送微信消息';} }//此處降低耦合 class Person {public function receive(SendMsg $send_msg) {return $send_msg->send();} }$person = new Person(); //靈活調(diào)用 echo $person->receive(new Email()); echo $person->receive(new WeChat());接口隔離原則: 還是為了降低耦合,減少接口類中臃腫的代碼段,一個(gè)類對(duì)另一個(gè)類的依賴應(yīng)該建立在最小的接口上,通俗的講就是需要什么就提供什么,不需要的就不要提供。比如接口類Test一共有func1,func2,func3,func4,func5 5個(gè)方法,A類依賴Test接口 func1,func2,func3三個(gè)方法,B類依賴Test接口 func1,func2,func4三個(gè)方法。這樣的接口func5就沒有用上,應(yīng)該做接口拆分,拆分成A類和B類依賴的接口僅僅夠用程度的接口即可,并且增加安全性,這里的拆,就是具有隔離不需要的代碼的作用。
<?php //接口定義了5個(gè)方法 interface TestInterface {public function func1();public function func2();public function func3();public function func4();public function func5(); }//A類依賴了接口中123三個(gè)方法,但func4 和 func5用不上 class A implements TestInterface {public function func1() { return '實(shí)現(xiàn)了TestInterface接口的func1方法';}public function func2() { return '實(shí)現(xiàn)了TestInterface接口的func2方法';}public function func3() { return '實(shí)現(xiàn)了TestInterface接口的func3方法';}public function func4() { return '這兩個(gè)方法用不上,但是由于PHP語法問題,需要去實(shí)現(xiàn)';}public function func5() { return '這兩個(gè)方法用不上,但是由于PHP語法問題,需要去實(shí)現(xiàn)';} }//A類依賴了接口中124三個(gè)方法,但func3 和 func5用不上 class B implements TestInterface {public function func1() { return '實(shí)現(xiàn)了TestInterface接口的func1方法';}public function func2() { return '實(shí)現(xiàn)了TestInterface接口的func2方法';}public function func3() { return '這兩個(gè)方法用不上,但是由于PHP語法問題,需要去實(shí)現(xiàn)';}public function func4() { return '實(shí)現(xiàn)了TestInterface接口的func4方法';}public function func5() { return '這兩個(gè)方法用不上,但是由于PHP語法問題,需要去實(shí)現(xiàn)';} }再看不臃腫的代碼:
<?php interface TestInterface_A {public function func1();public function func2();public function func3(); }interface TestInterface_B {public function func1();public function func2();public function func4(); }//A類實(shí)現(xiàn)了接口中123三個(gè)方法,無需依賴額外的方法 class A implements TestInterface_A {public function func1() { return '實(shí)現(xiàn)了TestInterface接口的func1方法';}public function func2() { return '實(shí)現(xiàn)了TestInterface接口的func2方法';}public function func3() { return '實(shí)現(xiàn)了TestInterface接口的func3方法';} }//A類實(shí)現(xiàn)了接口中124三個(gè)方法,無需依賴額外的方法 class B implements TestInterface_B {public function func1() { return '實(shí)現(xiàn)了TestInterface接口的func1方法';}public function func2() { return '實(shí)現(xiàn)了TestInterface接口的func2方法';}public function func4() { return '實(shí)現(xiàn)了TestInterface接口的func4方法';} }合成復(fù)用原則: 多用組合(has-a),少用繼承(is-a),可以降低類與類之間的耦合程度。遇見額外增加的功能,需要擴(kuò)展,通過關(guān)聯(lián),而不是繼承。因?yàn)槭褂美^承,后期改父級(jí)代碼可能會(huì)株連子級(jí),引起錯(cuò)誤。
繼承復(fù)用又稱之為白箱復(fù)用,組合聚合使用稱之為黑箱復(fù)用。
迪米特法則/最少知道原則 一個(gè)軟件實(shí)體盡可能少的與其它實(shí)體發(fā)生作用,限制了編程中通信的寬度和深度,不管依賴的類有多么的復(fù)雜,都盡量把邏輯封裝到類內(nèi)部,除了對(duì)外提供public的方法,不對(duì)外泄露任何無關(guān)信息。只和朋友通信,朋友通常是當(dāng)前類、當(dāng)前對(duì)象、成員屬性,成員方法參數(shù),成員方法返回值這些。其余的都是陌生人,不可直接調(diào)用。
法則又可以分為狹義法則和廣義法則。狹義的說法是:類A和類B發(fā)生關(guān)聯(lián),類B和類C發(fā)生關(guān)聯(lián),那么類A和類C是不能直接訪問的,需要通過B。優(yōu)點(diǎn)是降低耦合,缺點(diǎn)是需要大量的局部化設(shè)計(jì),讓類A可以訪問類C,造成模塊間使用效率降低。廣義的說法是:盡量創(chuàng)建松耦合的類,控制信息的過載,類之間的耦合度越低,越有利于復(fù)用,但是需要更高的抽象分離思想。
并由此出現(xiàn)了23種設(shè)計(jì)模式,設(shè)計(jì)模式用于解決經(jīng)典場景下的經(jīng)典問題而出來的通用實(shí)用規(guī)范。但23種有些并不適用于PHP語言,一旦強(qiáng)制使用,就缺失了弱類型語言的優(yōu)點(diǎn)。
擴(kuò)展
面向過程
面向過程是依事件為中心,分析出解決問題的步驟,將代碼分成若干個(gè)過程/函數(shù),一步步實(shí)現(xiàn),其中,函數(shù)或過程是最小的模塊封裝單位,然后調(diào)用這些函數(shù),可以稱之為方法。所謂的通俗講封裝就是把一堆代碼括起來。
面向?qū)ο笠彩腔诿嫦蜻^程的思想,面向?qū)ο髮?shí)現(xiàn),也必定有相應(yīng)的過程,所以有很多相同點(diǎn)。
過程與函數(shù)的區(qū)別
過程: 無返回值。
函數(shù): 有返回值。
類之間的6種關(guān)系:繼承、依賴、關(guān)聯(lián)、聚合、組合、泛化、實(shí)現(xiàn)
繼承: 兒子繼承爸爸,子類可以使用父類的任何非私有成員方法或成員屬性,且可以隨意擴(kuò)展,并且可以通過子類重寫放寬對(duì)資源的訪問修飾,且可以重寫父類非私有的的方法或成員屬性。很好理解,不多解釋。
依賴: 假設(shè)有兩個(gè)類:A和B,類B的某個(gè)成員方法的參數(shù)有類A,則類B依賴類A (也就是常說的uses-a,這里應(yīng)該是uses-b)。
<?php class A {public function one() {return 'one';} } class B {public function two(A $a) {return $a->one();} } $b_obj = new B(); echo $b_obj->two(new A());關(guān)聯(lián): 強(qiáng)依賴關(guān)系,當(dāng)所依賴的類已經(jīng)成為了類成員的時(shí)候,此時(shí)會(huì)被加載到內(nèi)存當(dāng)中的,而不是參數(shù)的時(shí)候(如果是單純的依賴,那么依賴所在的方法如果不調(diào)用,就不會(huì)發(fā)生什么),關(guān)聯(lián)有一對(duì)多,多對(duì)多之分,也有單向雙向之分,比如單向一對(duì)多。
<?php class A {public function one() {return 'one';} } class B {public $obj_a;public function two() {$this->obj_a = new A();return $this->obj_a->one();} } $b_obj = new B(); print_r($b_obj->two());聚合: A和B的關(guān)系是整體和局部的關(guān)系(has-a),整體和局部是可以分開的。DemoDateTime包含了DemoDate和DemoTime,也就是DemoDateTime聚合了DemoDate和DemoTime。
<?php class DemoDate {public function getDate() {return date("Y/m/d");} } class DemoTime {public function getTime() {return date("H:i:s");} }class DemoDateTime {public function getDateTime() {$date_obj = new DemoDate();$time_obj = new DemoTime();$datetime = $date_obj->getDate() . ' ' . $time_obj->getTime();return $datetime;} } $datetime_obj = new DemoDateTime(); echo $datetime_obj->getDateTime();組合: 組合關(guān)系也是整體和部分的關(guān)系,但它是強(qiáng)聚合關(guān)系。可以理解為各個(gè)部分不能離開整體,離開了就要出問題。
泛化
就是繼承。
實(shí)現(xiàn)
實(shí)現(xiàn)就是一個(gè)抽象類被其它類實(shí)現(xiàn)。
面向接口編程
通俗講就是多用用接口,把接口作為定義大骨架,來使用,剩下的細(xì)節(jié),交給實(shí)現(xiàn)接口的類去使用,也就是將定義與實(shí)現(xiàn)分離,需要開發(fā)者擁有分離的抽象思想。
什么時(shí)候使用抽象類什么時(shí)候使用接口?
(以上內(nèi)容為原創(chuàng),以下內(nèi)容全部來源于網(wǎng)絡(luò),然后進(jìn)行整合,感謝各位網(wǎng)友的回復(fù)。)
接口應(yīng)有兩類:第一類是對(duì)一個(gè)體的抽象,它可對(duì)應(yīng)為一個(gè)抽象體(abstract class);第二類是對(duì)一個(gè)體某一方面的抽象,即形成一個(gè)抽象面(interface),一個(gè)體有可能有多個(gè)抽象面。
操作數(shù)據(jù)庫就必須會(huì)用到 Insert Update Select ,所以Insert Update Select 做成接口
但是,每個(gè)功能操作的內(nèi)容又不一樣,所以,做一個(gè)抽象類繼承接口然后抽象類的派生類去實(shí)現(xiàn)抽象類的具體方法。
接口是一組規(guī)則的集合,它規(guī)定了實(shí)現(xiàn)本接口的類或接口必須擁有的一組規(guī)則
抽象類和接口的區(qū)別在于使用動(dòng)機(jī)。使用抽象類是為了代碼的復(fù)用,而使用接口的動(dòng)機(jī)是為了實(shí)現(xiàn)多態(tài)性。
如果這個(gè)概念在我們腦子中是確確實(shí)實(shí)存在的,就用抽象類。
否則的話,如果這個(gè)概念僅僅是一方面的特性,比如會(huì)飛的,能跑的,這些我們就設(shè)置為接口。
兩個(gè)概念模糊,不知道設(shè)置為抽象類還是接口的時(shí)候,一般我們設(shè)置為接口,原因是我們實(shí)現(xiàn)了這個(gè)接口還可以繼承。
抽象類適合用來定義某個(gè)領(lǐng)域的固有屬性,也就是本質(zhì),接口適合用來定義某個(gè)領(lǐng)域的擴(kuò)展功能。
當(dāng)需要為一些類提供公共的實(shí)現(xiàn)代碼時(shí),應(yīng)優(yōu)先考慮抽象類。因?yàn)槌橄箢愔械姆浅橄蠓椒梢员蛔宇惱^承下來,使實(shí)現(xiàn)功能的代碼更簡單。
當(dāng)注重代碼的擴(kuò)展性跟可維護(hù)性時(shí),應(yīng)當(dāng)優(yōu)先采用接口。①接口與實(shí)現(xiàn)它的類之間可以不存在任何層次關(guān)系,接口可以實(shí)現(xiàn)毫不相關(guān)類的相同行為,比抽象類的使用更加方便靈活;②接口只關(guān)心對(duì)象之間的交互的方法,而不關(guān)心對(duì)象所對(duì)應(yīng)的具體類。接口是程序之間的一個(gè)協(xié)議,比抽象類的使用更安全、清晰。一般使用接口的情況更多。
當(dāng)描述一組方法的時(shí)候使用接口
當(dāng)描述一個(gè)虛擬的物體的時(shí)候使用抽象類
抽象類是跟繼承類是“is”的關(guān)系,接口和實(shí)現(xiàn)類是"like"的關(guān)系,從這個(gè)角度出發(fā),應(yīng)該可以看出很多東西。其它的區(qū)別只是一些語法,用法規(guī)范上的區(qū)別,核心思想就是這個(gè)is-like區(qū)別。
總結(jié)
以上是生活随笔為你收集整理的深入理解面向对象,面向对象3个特性7个原则6种关系的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: windows 开机速度优化
- 下一篇: win10 jdk1.8迅雷下载