再見耳幢,面向?qū)ο缶幊蹋岸晦。℅oodbye, Object Oriented Programming)

左耳聽風 第一周 Review.png

原文:Goodbye, Object Oriented Programming
原作者:Charles Scalfani

原文介紹

作為使用面向?qū)ο缶幊桃延惺畮啄甑淖髡撸?jīng)也非常熱衷于繼承睛藻、封裝启上、多態(tài)的優(yōu)點。它們是范式的三大支柱店印。但是隨著對面向?qū)ο缶幊痰纳钊肜斫飧栽冢Y(jié)合實際遇到的問題,帶著挑剔的眼光按摘,作者看到了面向?qū)ο缶幊痰囊恍﹩栴}包券。

*繼承,墮落的第一支柱

**香蕉 猴 叢林問題

你想要一個香蕉炫贤,但是你得到的卻是一只拿著香蕉的大猩猩和整個叢林溅固。

由于繼承原因,當你想要復用其他項目某一個已經(jīng)存在的類時兰珍,你不得不需要這個類的父類侍郭,然后可能它父類的父類……最后會發(fā)現(xiàn)我需要它的祖宗十八代=。=。你以為解決了這個就可以了嗎亮元?少年你還是太天真了猛计,你會發(fā)現(xiàn),它編譯不過爆捞,為什么奉瘤? 這個對象包含了這個其他對象。 所以你也需要它煮甥,這沒問題毛好,但問題是你不只是需要那個對象,你需要對象的父對象及其父對象的父對象苛秕,依此類推肌访,每個包含的對象以及包含父對象,父對象艇劫,父對象的所有父對象......

**香蕉猴叢林解決方案

我們可以通過不創(chuàng)建太深的層次結(jié)構(gòu)來解決這個問題吼驶。哦……似乎沒什么不對,但如果繼承是重用的關(guān)鍵店煞,那么我對該機制的任何限制肯定會限制重用的好處蟹演。

**鉆石問題(菱形繼承問題)

早晚你會遇到下面這種惡心的問題,有些語言甚至根本解決不了顷蟀。


image.png

大多數(shù)面向?qū)ο笳Z言都不支持這種情況酒请,盡管看上去似乎很符合邏輯。為什么面向?qū)ο笳Z言支持這種情況如此困難鸣个?

Class PoweredDevice {
}
Class Scanner inherits from PoweredDevice {
  function start() {
  }
}
Class Printer inherits from PoweredDevice {
  function start() {
  }
}
Class Copier inherits from Scanner, Printer {
}

請注意羞反,Scanner類和Printer類都實現(xiàn)了一個名為start的函數(shù)。那么Copier類繼承了哪個啟動函數(shù)囤萤? 掃描儀在昼窗? 打印機一個? 它不可能兩者兼而有之涛舍。

**鉆石(菱形繼承問題)解決方案

解決方案很簡單:不要這樣做澄惊。沒錯。大多數(shù)面向?qū)ο蠖疾蛔屇氵@么干富雅。但是掸驱,但是……要是必須這樣建模該怎么辦?我需要重用没佑!那就必須使用包含和委托毕贼。

Class PoweredDevice {
}
Class Scanner inherits from PoweredDevice {
  function start() {
  }
}
Class Printer inherits from PoweredDevice {
  function start() {
  }
}
Class Copier {
  Scanner scanner
  Printer printer
  function start() {
    printer.start()
  }
}

這里注意Copier類現(xiàn)在包含了Printer類和Scanner類的實例。Copier委派Printer類的start方法去實現(xiàn)自己的start方法图筹,它也可以簡單的委派給Scanner類的start方法帅刀。
在C++我們可以采用虛繼承让腹,這個網(wǎng)上搜索下很多講解。

**脆弱的基類問題

我們盡量使用較淺的類層次結(jié)構(gòu)扣溺,并保證里面沒有環(huán)骇窍,這樣就不會出現(xiàn)菱形繼承了。
似乎一切都解決了锥余。直到我們發(fā)現(xiàn)……
我前一天工作得好好的代碼今天出錯了腹纳!關(guān)鍵是,我沒有改任何代碼驱犹!
嗯也許是個 bug……但等等……的確有些改動……
但改動的不是我的代碼嘲恍。似乎改動來自我繼承的那個類。
基類的改動怎么讓我的代碼掛了呢雄驹?
看看下面這個基類

import java.util.ArrayList;
 
public class Array
{
  private ArrayList<Object> a = new ArrayList<Object>();
 
  public void add(Object element)
  {
    a.add(element);
  }
 
  public void addAll(Object elements[])
  {
    for (int i = 0; i < elements.length; ++i)
      a.add(elements[i]); // this line is going to be changed
  }
}

注意被注釋的那一行佃牛,那一行改動會讓代碼掛掉。
下面是派生類

public class ArrayCount extends Array
{
  private int count = 0;

  @Override
  public void add(Object element)
  {
    super.add(element);
    ++count;
  }

  @Override
  public void addAll(Object elements[])
  {
    super.addAll(elements);
    count += elements.length;
  }
}

Array的add()添加一個元素到本地的ArrayList
Array的addAll()調(diào)用本地的ArrayList的add()方法添加循環(huán)的每一個元素医舆。
ArrayCount的add()方法調(diào)用了父類的add()然后count變量+1俘侠。
ArrayCount的addAll()方法調(diào)用父類的addAll()然后加上元素數(shù)組的長度。
基類中加注釋的那行代碼現(xiàn)在改成這樣,問題就出現(xiàn)了:

 public void addAll(Object elements[])
  {
    for (int i = 0; i < elements.length; ++i)
      add(elements[i]); // this line was changed
  }

從基類的作者的角度來看蔬将,這個類實現(xiàn)的功能完全沒有變化爷速。而且所有自動化測試也都通過來了。但是基類的作者忘記了繼承的類霞怀。而繼承類的作者就被坑了T_T惫东。

現(xiàn)在ArrayCount的addAll()調(diào)用父類的addAll(),后者在內(nèi)部調(diào)用add()毙石,而add()被繼承類重載了廉沮。
因此,每次繼承類的add()被調(diào)用時胁黑,count都會增加废封,然后在繼承類的addAll()被調(diào)用時再次增加。
count被增加了兩次丧蘸。

**脆弱的基類的解決方法

這個問題還得要包含和委托來解決。使用包含和委托遥皂,可以從白盒編程轉(zhuǎn)到黑盒編程力喷。白盒編程的意思是說,寫繼承類時必須要了解基類的實現(xiàn)演训。而黑盒編程可以完全無視基類的實現(xiàn)弟孟,因為不可能通過重載函數(shù)的方式向基類注入代碼。只需要關(guān)注接口即可样悟。

*封裝拂募,倒塌的第二根支柱

封裝似乎是面向?qū)ο缶幊痰牡诙蠛锰幫バ伞ο鬆顟B(tài)變量被保護起來防止外部訪問,即它們被封裝在對象內(nèi)部陈症。我們不需要再操心那些可能被不知道誰訪問的全局變量蔼水。但是:

**引用問題

為了提高效率,對象傳遞給函數(shù)時傳遞的是引用录肯,而不是值趴腋。也就是說,函數(shù)不會傳遞對象本身论咏,而是傳遞指向?qū)ο蟮囊粋€引用或指針优炬。
如果一個對象的引用被傳遞給另一個對象的構(gòu)造函數(shù),構(gòu)造函數(shù)就能將這個對象引用放到私有變量中厅贪,用封裝保護起來蠢护。
但這個傳遞的對象不是安全的!因為其他代碼也可能擁有指向該對象的指針养涮,比如調(diào)用構(gòu)造函數(shù)的那段代碼糊余。它必須有指向?qū)ο蟮囊茫駝t沒辦法傳遞給構(gòu)造函數(shù)单寂。

**引用的解決

構(gòu)造函數(shù)必須要復制傳遞過來的對象贬芥。而且不能是淺復制,必須是深復制宣决,即傳入的對象內(nèi)包含的所有對象和所有對象中包含的所有對象……都必須要復制蘸劈。但這卻沒有效率。更糟糕的是尊沸,并非所有對象都能復制的威沫。一些擁有操作系統(tǒng)資源的對象,最好的情況是復制無效洼专,最糟糕的情況是根本不可能復制棒掠。

*多態(tài),倒塌的第三根支柱

并不是因為多態(tài)不好屁商,而是因為實現(xiàn)多態(tài)并不需要面向?qū)ο笳Z言烟很。接口也能實現(xiàn)多態(tài),而且不需要面向?qū)ο蟮呢摀狻6椅砀ぃ涌谝膊粫拗颇隳芑烊氲牟煌袨榈臄?shù)目」倩梗可以告別面向?qū)ο蟮亩鄳B(tài)芹橡,使用基于接口的多態(tài)。

個人觀點:

作者羅列了面向?qū)ο缶幊痰亩鄠€問題:香蕉 猴 叢林問題望伦、鉆石問題(菱形繼承問題)林说、脆弱的基類問題煎殷、引用問題。并且可以使用基于接口的多態(tài)而非面向?qū)ο蟮亩鄳B(tài)腿箩。這是作者幾十年面向?qū)ο缶幊痰慕?jīng)驗之談豪直,但是 說 再見,面向?qū)ο缶幊?肯定是夸大了度秘,不過其問題與不足我們也該謹慎處理避免掉坑里顶伞。也許隨著歷史的發(fā)展會出現(xiàn)更好的更NB的不同理念的編程語言,即使那時我相信面向?qū)ο缶幊桃矔幸幌亍?/p>


知乎個人首頁:
https://www.zhihu.com/people/lichangke/
個人Blog:
https://lichangke.github.io/
歡迎大家來一起交流學習

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末剑梳,一起剝皮案震驚了整個濱河市唆貌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌垢乙,老刑警劉巖锨咙,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異追逮,居然都是意外死亡酪刀,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門钮孵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來骂倘,“玉大人,你說我怎么就攤上這事巴席±裕” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵漾唉,是天一觀的道長荧库。 經(jīng)常有香客問我,道長赵刑,這世上最難降的妖魔是什么分衫? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮般此,結(jié)果婚禮上蚪战,老公的妹妹穿的比我還像新娘。我一直安慰自己恤煞,他們只是感情好屎勘,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著居扒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪丑慎。 梳的紋絲不亂的頭發(fā)上喜喂,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天瓤摧,我揣著相機與錄音,去河邊找鬼玉吁。 笑死照弥,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的进副。 我是一名探鬼主播这揣,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼影斑!你這毒婦竟也來了给赞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤矫户,失蹤者是張志新(化名)和其女友劉穎片迅,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體皆辽,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡柑蛇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了驱闷。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耻台。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖空另,靈堂內(nèi)的尸體忽然破棺而出盆耽,到底是詐尸還是另有隱情,我是刑警寧澤痹换,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布征字,位于F島的核電站,受9級特大地震影響娇豫,放射性物質(zhì)發(fā)生泄漏匙姜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一冯痢、第九天 我趴在偏房一處隱蔽的房頂上張望氮昧。 院中可真熱鬧,春花似錦浦楣、人聲如沸袖肥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽椎组。三九已至,卻和暖如春历恐,著一層夾襖步出監(jiān)牢的瞬間寸癌,已是汗流浹背专筷。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蒸苇,地道東北人磷蛹。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像溪烤,于是被迫代替她去往敵國和親味咳。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容