欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

面向?qū)ο笕筇匦缘囊饬x講解

 更新時間:2018年12月18日 14:31:43   作者:李先靜  
今天小編就為大家分享一篇關(guān)于面向?qū)ο笕筇匦缘囊饬x講解,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧

面向?qū)ο蟮娜筇匦裕悍庋b、繼承和多態(tài)。這是任何一本面向?qū)ο笤O(shè)計的書里都會介紹的,但鮮有講清楚的,新手看了之后除了記住幾個概念外,并沒真正了解他們的意義。前幾天在youtube上看了Bob大叔講解的SOLID原則,其中有一段提到面向?qū)ο蟮娜筇匦?,收獲很多,但是我并不完全贊同他的觀點,這里談?wù)勎业南敕ǎ?/p>

封裝

『封裝』第一層含義是信息隱藏。這是教科書里都會講解的,把類或模塊的實現(xiàn)細(xì)節(jié)隱藏起來,對外只提供最小的接口,也就是所謂的『最小知識原則』。有個共識,正常的程序員能理解的代碼在一萬行左右。這是指在理解代碼的實現(xiàn)細(xì)節(jié)的情況下,正常的程序員能理解的代碼的規(guī)模。比如一個文件系統(tǒng),F(xiàn)AT、NTFS、EXT4和YAFFS2等,它們的實現(xiàn)是比較復(fù)雜的,少則幾千行代碼,多則幾萬行,要理解它們的內(nèi)部實現(xiàn)是很困難的,但是如果屏蔽它們的內(nèi)部實現(xiàn)細(xì)節(jié),只是要了解它們對外的接口,那就非常容易了。

關(guān)于『封裝』的這一層含義,Bob大叔提出了驚人的見解:『封裝』不是面向?qū)ο蟮奶匦?,面向過程的C語言比面向?qū)ο蟮腃++/Java在『封裝』方面做得更好!證據(jù)也是很充分:C語言把函數(shù)的分為內(nèi)部函數(shù)和外部函數(shù)兩類。內(nèi)部函數(shù)用static修飾,放在C文件中,外部函數(shù)放在頭文件中。你完全不知道內(nèi)部函數(shù)的存在,即使知道也沒法調(diào)用。而像在C++/Java中,通過public/protected/private/friend等關(guān)鍵字,把函數(shù)或?qū)傩苑殖刹煌牡燃?,這把內(nèi)部的細(xì)節(jié)暴露給使用者了,使用者甚至可以繞過編譯器的限制去調(diào)用私有函數(shù)。所以在信息隱藏方面,『封裝』不但不是面向?qū)ο蟮奶匦?,而且面向?qū)ο鬁p弱了『封裝』。

『封裝』的第二層含義是把數(shù)據(jù)和行為封裝在一起。我覺得這才是面向?qū)ο笾械摹悍庋b』的意義所在,而一般的教科書里并沒提及或強調(diào)。面向過程的編程中,數(shù)據(jù)和行為是分離的,面向?qū)ο蟮木幊虅t是把它們看成一個有機的整體。所以,從這一層含義來看,『封裝』確實是面向?qū)ο蟮摹禾匦浴弧?/p>

面向?qū)ο笫且环N思維方式,而不是表現(xiàn)形式。在C語言中,可以實現(xiàn)面向?qū)ο蟮木幊?,事實上,幾乎所有C語言開發(fā)的大型項目,都是采用了面向?qū)ο蟮乃枷腴_發(fā)的。把C語言說成面向過程的語言是不公平的,是不是面向?qū)ο蟮木幊讨饕强粗笇?dǎo)思想,而不是編程語言。你用C++/Java可以寫面向過程的代碼,也可以用C語言寫面向?qū)ο蟮拇a。

繼承

類就是分類的標(biāo)準(zhǔn),也就是一類事物,一類具有相同屬性和行為對象的抽象。比如動物就是一個類,它描述了所有具有動物這個屬性的事物的集合。狗也是一個類,它具有動物所有的特性,我們說狗這個類繼承了動物這個類,動物是狗的父類,狗是動物的子類。在C語言中也可以模擬繼承的效果,比如:

struct Animal {
...
};
struct Dog {
  struct Animal animal;
  ...
}
struct Cat {
  struct Animal animal;
  ...
}

因為C語言也可以實現(xiàn)『繼承』,所以Bob大叔認(rèn)為『繼承』也不算不上是面向?qū)ο蟮摹禾匦浴?。但是我覺得,C語言中實現(xiàn)『繼承』的方式,需要用面向?qū)ο蟮乃季S來思考才能理解,否則純粹從數(shù)據(jù)結(jié)構(gòu)的方式來看上面的例子,理解起來就會大相徑庭:animal是Dog的一個成員,所以Animal可以看成是Dog的一部分!Is a 變成了has a。只有在面向?qū)ο蟮乃枷胫?,說『繼承』才有意義,所以說『繼承』是面向?qū)ο蟮摹禾匦浴徊⒉粻繌姟?/p>

在C語言里實現(xiàn)多重繼承更是非常麻煩了,記得glib里實現(xiàn)了接口的多重繼承,但是用起來還是挺別扭的,對新手來說更是難以理解。多重繼承在某些情況下,會對編譯器造成歧義,比菱形繼承結(jié)構(gòu):A是基類,B和C是它的兩個子類,D從B和C中繼承過來,如果B和C都重載了A的一個函數(shù),編譯器此時就沒法區(qū)分用B的還是C的了(當(dāng)然這是可以解決的)。

像Bob大叔說的,Java沒有實現(xiàn)多重繼承,并不是多重繼承沒有用。而是為了簡化編譯器的實現(xiàn),C#沒有實現(xiàn)多重繼承,則是因為Java沒有實現(xiàn)多重繼承:)

除了接口多重繼承是必不可少的,類的多重繼承在現(xiàn)實中也是很常見的。比如:狼和狗都是狗科動物的子類,貓和老虎都是貓科動物的子類。狗科動物和貓科動物都是動物的子類。但是貓和狗都是家畜,老虎和狼都是野生動物。貓不但要繼承貓科動物的特性,還繼承家畜的特性。類就是分類的標(biāo)準(zhǔn),而混用不同的分類標(biāo)準(zhǔn)是多重繼承的主要來源。多重繼承可以用其他方式實現(xiàn),比如traits和mixin。

不管是普通繼承,接口繼承,還是多重繼承,在面向?qū)ο蟮木幊陶Z言中,實現(xiàn)起來要更加容易和直觀,在面向過程的語言中,雖然可以實現(xiàn),但是比較丑陋,而且本質(zhì)是面向?qū)ο蟮乃伎挤绞?。所以『繼承』應(yīng)該稱得上是面向?qū)ο蟮摹禾匦浴涣恕=橛诶^承帶來的復(fù)雜性,現(xiàn)代面向?qū)ο蟮脑O(shè)計中,都推薦用組合來代替繼承實現(xiàn)重用。

多態(tài)

『多態(tài)』本來是面向?qū)ο笏枷胫凶钪匾男再|(zhì)(當(dāng)然也算不上是特有的性質(zhì)),但是教科書里都只是介紹了『多態(tài)』的表現(xiàn)形式,而沒有介紹它用途和價值?!憾鄳B(tài)』一般表現(xiàn)為兩種形式:

允許不同輸入?yún)?shù)的同名函數(shù)存在。這個性質(zhì)會帶來一定的便利,特別是對于構(gòu)造函數(shù)和操作符的重載。但這種『多態(tài)』是在編譯時就確定了的,所以只能算成一種語法糖,并沒有什么特別的意義。

子類可以重載父類中函數(shù)原型完全相同的同名函數(shù)。如果只看它的表現(xiàn)形式,在父類中存在的函數(shù),在不同的子類中可以被重新實現(xiàn),這看起來是吃飽了撐著。但是這種『多態(tài)』卻是軟件架構(gòu)的基礎(chǔ),幾乎所有的設(shè)計模式和方法都依賴這種特性。

隔離變化是軟件架構(gòu)設(shè)計的基本目標(biāo)之一,接口正是隔離變化最重要的手段。我們經(jīng)常說分離接口與實現(xiàn),針對接口編程,主要是因為接口可以隔離變化。如果沒有第二種『多態(tài)』,就沒有真正意義上的接口。面向?qū)ο笾械慕涌冢粌H是指模塊對外提供的一組函數(shù),而且特指在運行時才綁定具體實現(xiàn)的一組函數(shù),在編譯時根本不知道這組函數(shù)是誰提供的。我們先把接口簡單的理解為,在基類中定義一組函數(shù),但是基類并沒有實現(xiàn)它們,而在具體的子類中去實現(xiàn)。這不就是『多態(tài)』的第二種表現(xiàn)形式么。

接口怎么能夠隔離變化呢?Bob大叔舉了一個非常好的例子:

#include <stdio.h>
int main() {
  int c;
  while((c = getchar()) != EOF) {
    putchar(c);
  }
  return 0;
}

這個程序和Hello world是一個級別的,你從鍵盤輸入一個字符,它就顯示一個字符。但是它卻蘊含了『多態(tài)』最精妙的招式。比如說輸入吧,getchar是從標(biāo)準(zhǔn)輸入(STDIN)讀入一個字符,鍵盤輸入是缺省的標(biāo)準(zhǔn)輸入,但是鍵盤輸入只是眾多標(biāo)準(zhǔn)輸入(STDIN)中的一種。你可以從任何一個IO設(shè)備讀取數(shù)據(jù):從網(wǎng)絡(luò)、文件、內(nèi)存和串口等等,換成任何一種輸入,這個程序都不需要任何改變。

具體實現(xiàn)變了,調(diào)用者不需要修改代碼,而且它根本不用重新編譯,甚至不用重啟應(yīng)用程序。這就是接口的威力,也是『多態(tài)』的功勞。

上面的程序是如何做到的呢?IO設(shè)備的驅(qū)動是一套接口,它定義了打開、關(guān)閉、讀和寫等操作。對實現(xiàn)者來說,不管數(shù)據(jù)從哪里來,要到哪里去,只要實現(xiàn)接口中定義的函數(shù)即可。對使用者來說,完全不同關(guān)心它具體的實現(xiàn)方式。

『多態(tài)』不但是隔離變化的基礎(chǔ),也是代碼重用的基礎(chǔ)。公共函數(shù)的重用是有價值的,在面向過程的開發(fā)中也很容易做到這種重用。但現(xiàn)實中的重用沒那么簡單,就連一些大師也感嘆重用太難。比如,你可能需要A這個類,你把它拿過來時,發(fā)現(xiàn)它有依賴B這個類,B這個類有依賴C這個類,搞到最后發(fā)現(xiàn),它還依賴一個看似完全不相關(guān)的類,重用的念頭只好打住。如果你覺得夸張了,你可以嘗試從一個數(shù)據(jù)庫(如sqlite)中,把它的B+樹代碼拿出來用一下。

在『多態(tài)』的幫助下,情況就會大不相同了。A這個類依賴于B這個類,我們可以把B定義成一個接口,讓使用A這個類的使用者傳入進來,也就是所謂的依賴注入。如果你想重用A這個類,你可以為它定制一個B接口的實現(xiàn)。比如,我最近在一個只有8K內(nèi)存的硬件上,為一塊norflash寫了一個簡單的文件系統(tǒng)(且看作是A類),如果我直接去調(diào)用norflash的API(且看作是B類),就會讓文件系統(tǒng)(A類)與norflash的API(B類)緊密耦合到一起,這就會讓文件系統(tǒng)的重用性大打折扣。

我的做法是定義了一個塊設(shè)備的接口(即B接口):

typedef unsigned short block_num_t;
struct _block_dev_t;
typedef struct _block_dev_t block_dev_t;
typedef block_num_t (*block_dev_get_block_nr_t)(block_dev_t* dev);
typedef bool_t (*block_dev_read_block_t)(block_dev_t* dev, block_num_t block_num, void* buff);
typedef bool_t (*block_dev_write_block_t)(block_dev_t* dev, block_num_t block_num, const void* buff);
typedef void  (*block_dev_destroy_t)(block_dev_t* dev);
struct _block_dev_t {
  block_dev_get_block_nr_t  get_block_nr;
  block_dev_write_block_t  write_block;
  block_dev_read_block_t   read_block;
  block_dev_destroy_t    destroy;
};

在初始化文件系統(tǒng)時,把塊設(shè)備注入進來:

bool_t sfs_init(sfs_t* fs, block_dev_t* dev);

這樣,文件系統(tǒng)只與塊設(shè)備接口交互,不需要關(guān)心實現(xiàn)是norflash、nandflash、內(nèi)存還是磁盤。而且?guī)韼讉€附加好處:

可以在PC上做文件系統(tǒng)的單元測試。在PC上,用內(nèi)存模擬一個塊設(shè)備,文件系統(tǒng)可以正常工作了。

可以通過裝飾模式為塊設(shè)備添加磨損均衡算法和壞塊管理算法。這些算法和文件系統(tǒng)都可以獨立重用。

『多態(tài)』讓真正的重用成為可能,沒有『多態(tài)』就沒有各種框架。在C語言中,多態(tài)是通過函數(shù)指針實現(xiàn)的,而在C++中是通過虛函數(shù),在Java中有專門的接口,在JS這種動態(tài)語言中,每個函數(shù)是多態(tài)的。『多態(tài)』雖然不是面向?qū)ο蟮摹禾赜械摹粚傩?,但是面向?qū)ο蟮木幊陶Z言讓『多態(tài)』更加簡單和安全。

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對腳本之家的支持。如果你想了解更多相關(guān)內(nèi)容請查看下面相關(guān)鏈接

相關(guān)文章

  • OpenCV中C++函數(shù)imread讀取圖片的問題及解決方法

    OpenCV中C++函數(shù)imread讀取圖片的問題及解決方法

    利用C++函數(shù)imread讀取圖片的時候返回的結(jié)果總是空,而利用C函數(shù)cvLoadImage時卻能讀取到圖像。怎么回事?今天小編通過本教程給大家簡單說明原因
    2017-03-03
  • C++11中的智能指針shared_ptr、weak_ptr源碼解析

    C++11中的智能指針shared_ptr、weak_ptr源碼解析

    本文是基于gcc-4.9.0的源代碼進行分析,shared_ptr和weak_ptr是C++11才加入標(biāo)準(zhǔn)的,僅對C++智能指針shared_ptr、weak_ptr源碼進行解析,需要讀者有一定的C++基礎(chǔ)并且對智能指針有所了解
    2021-09-09
  • C語言實現(xiàn)頁面置換算法

    C語言實現(xiàn)頁面置換算法

    這篇文章主要為大家詳細(xì)介紹了C語言實現(xiàn)頁面置換算法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • C/C++利用棧和隊列實現(xiàn)停車場管理系統(tǒng)

    C/C++利用棧和隊列實現(xiàn)停車場管理系統(tǒng)

    數(shù)據(jù)結(jié)構(gòu)的課程設(shè)計一般都不是很好理解,今天小編為大家總結(jié)了一下c和c++版本的常見棧和隊列的的停車場管理程序,需要的小伙伴可以參考一下
    2022-06-06
  • C++中關(guān)于constexpr函數(shù)使用及說明

    C++中關(guān)于constexpr函數(shù)使用及說明

    這篇文章主要介紹了C++中關(guān)于constexpr函數(shù)使用及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • C語言簡明清晰講解枚舉

    C語言簡明清晰講解枚舉

    枚舉法的本質(zhì)就是從所有候選答案中去搜索正確的解,枚舉算法簡單粗暴,他暴力的枚舉所有可能,盡可能地嘗試所有的方法,感興趣的朋友來看看吧
    2022-05-05
  • C++解析obj模型文件方法介紹

    C++解析obj模型文件方法介紹

    由于本人打算使用Assimp來加載模型,這里記錄一下tinyobjloader庫的使用。之前也研究過fbxsdk,除了骨骼動畫暫未讀取外,代碼自認(rèn)為還算可靠
    2022-09-09
  • MFC程序設(shè)計常用技巧匯總

    MFC程序設(shè)計常用技巧匯總

    這篇文章主要介紹了MFC程序設(shè)計常用技巧,實例匯總了MFC程序設(shè)計中常見的問題與解決方法,非常具有實用價值,需要的朋友可以參考下
    2015-05-05
  • C++初識類和對象

    C++初識類和對象

    類是創(chuàng)建對象的模板,一個類可以創(chuàng)建多個對象,每個對象都是類類型的一個變量;創(chuàng)建對象的過程也叫類的實例化。每個對象都是類的一個具體實例(Instance),擁有類的成員變量和成員函數(shù)
    2021-10-10
  • C語言常量介紹

    C語言常量介紹

    這篇文章介紹了C語言中的常量,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-12-12

最新評論