計算機OSI模型與5G協議
一、面向物件是什麼
面向物件 (Object Oriented,OO) 的思想對軟體開發相當重要,它的概念和應用甚至已超越了程式設計和軟體開發,擴充套件到如資料庫系統、互動式介面、應用結構、應用平臺、分散式系統、網路管理結構、CAD 技術、人工智慧等領域。面向物件是一種 對現實世界理解和抽象的方法,是計算機程式設計技術發展到一定階段後的產物。
面向過程 (Procedure Oriented) 是一種 以過程為中心 的程式設計思想。這些都是以什麼正在發生為主要目標進行程式設計,不同於面向物件的是誰在受影響。與面向物件明顯的不同就是 封裝、繼承、類。
無論是在軟體開發還是在實際工作中,深入地理解軟體開發的思想都非常有必要。
二、從一場比賽說起
在一個軟體村裡
有一名資深「面向過程」程式設計師——老過
和一名「面向物件」信徒——阿對
同時受僱於一家挨踢店
有一天老闆突發奇想
決定讓這兩名程式設計師進行一次比賽
獲勝者將獲得一個限量的
360 度全自動按摩椅
程式設計比賽開始了
不一會,他倆都寫出了幾乎相同的程式碼
class Bill{ // 獲取總價 fun getPrice(): Double { val unit = getUnit() val number = getNumber() val price = unit * number return price } // 獲取單價 fun getUnit(): Double { ... } // 獲取數量 fun getNumber(): Int { ... } }
老過看到新需求,微微一笑
class Bill{ fun getPrice(): Double { val unit = getUnit() val number = getNumber() val price = unit * number if (todayIsLoversDay()) { return price * 0.77 } return price } fun getUnit(): Double { ... } fun getNumber(): Int { ... } fun todayIsLoversDay(): Boolean { ... } }
他決定讓新的收銀方式繼承 Bill 類
先在 Bill 類中新增 discount 方法
並將其開放
open class Bill{
fun getPrice(): Double {
val unit = getUnit()
val number = getNumber()
val price = unit * number
return discount(price)
}
// 處理打折優惠
open fun discount(price: Double): Double{
return price
}
fun getUnit(): Double {
...
}
fun getNumber(): Int {
...
}
}
普通的收費方式在 discount
函式中直接返回價格
七夕節的收費方式則繼承此類
在 discount 函式中實現打 77折
class LoversDayBill : Bill(){
override fun discount(price: Double): Double {
return price * 0.77
}
}
當老過和阿對同時將程式交給老闆時
老過已經開始幻想自己將來
坐在按摩椅上的舒服日子
聽到新需求
老過一陣頭大
不由在群裡吐槽
吐槽歸吐槽
老過在 getPrice 函式中
再次增加了條件判斷
class Bill {
fun getPrice(): Double {
val unit = getUnit()
val number = getNumber()
val price = unit * number
if (todayIsLoversDay()) {
return price * 0.77
}
if (todayIsMiddleAutumn() && price > 399) {
return price - 200
}
if (todayIsNationalDay() && price < 100) {
// 生成 0 ~ 9 隨機數字,如果為 0 則免單。即:十分之一概率免單
val free = Random().nextInt(10)
if (free == 0) {
return 0.0
}
}
return price
}
fun getUnit(): Double {
...
}
fun getNumber(): Int {
...
}
fun todayIsLoversDay(): Boolean {
...
}
fun todayIsMiddleAutumn(): Boolean {
...
}
fun todayIsNationalDay(): Boolean {
...
}
}
看著越來越複雜的
Bill 類和 getPrice 方法
老過眉頭緊鎖
他深知事情遠沒有結束
中秋和國慶一過
他還需要到這個複雜的類中
刪掉打折的方法
天知道老闆還會再提
什麼天馬行空的需求
無論是新增或刪除程式碼,在這個過長的類裡做修改都是件不太愉快的事。為了在一個很長的函式中找到需要修改的位置,「面向過程」使得老過不得不瀏覽大量與修改無關的程式碼,小心翼翼地修改後,又要反覆確認不會影響到類的其他部分。
老過在心底裡默默地祈禱
這個類不再需要修改
提交了自己的程式
阿對收到新需求時
先是新增了中秋節支付類
class MiddleAutumePrice : Bill() {
override fun discount(price: Double): Double {
if (price > 399) {
return price - 200
}
return super.discount(price)
}
}
再增加了國慶節支付類:
class NationalDayBill : Bill() {
override fun discount(price: Double): Double {
if (price < 100) {
// 生成 0 ~ 9 隨機數字,如果為 0 則免單。即:十分之一概率免單
val free = Random().nextInt(10)
if (free == 0) {
return 0.0
}
}
return super.discount(price)
}
}
「面向物件」讓阿對最喜歡的一點是
有一個好訊息要告訴大家!
當老闆興高采烈地說出這句話時
老過和阿對都露出了心驚膽戰的表情
這句話往往意味著要更改需求
老過反抗道
但他並沒有說出心裡另一個小九九
實在不想再去給 Bill 類新增程式碼了
這次修改老過花了較長的時間才完成
class Bill {
val gifts = listOf("flower", "chocolate", "9.9 discount")
fun getPrice(): Double {
val unit = getUnit()
val number = getNumber()
val price = unit * number
if (todayIsLoversDay() && isCouple()) {
if (price > 99) {
val lucky = Random().nextInt(gifts.size)
println("Congratulations on getting ${gifts[lucky]}!")
}
return price * 0.77
}
if (todayIsMiddleAutumn() && price > 399) {
return price - 200
}
if (todayIsNationalDay() && price < 100) {
// 生成 0 ~ 9 隨機數字,如果為 0 則免單。即:十分之一概率免單
val free = Random().nextInt(10)
if (free == 0) {
return 0.0
}
}
return price
}
fun getUnit(): Double {
...
}
fun getNumber(): Int {
...
}
fun todayIsLoversDay(): Boolean {
...
}
private fun isCouple(): Boolean {
...
}
fun todayIsMiddleAutumn(): Boolean {
...
}
fun todayIsNationalDay(): Boolean {
...
}
}
看著那個只屬於七夕節的 gifts 變數
老過像看著自己白襯衫上的油漬一樣難受
以後每次收費時都會生成一個
只有七夕節才會用到的變數
都是因為老闆的需求太奇葩
才讓這個程式看起來亂糟糟的
由於這個類做了修改
本來已經測試通過的程式碼又得重測一遍
阿對打開了 LoversDayBill 類
將其修改如下
class LoversDayBill : Bill() {
val gifts = listOf("flower", "chocolate", "9.9 discount")
override fun discount(price: Double): Double {
if (!isCouple()) return price
if (price > 99) {
val lucky = Random().nextInt(gifts.size)
println("Congratulations on getting ${gifts[lucky]}!")
}
return price * 0.77
}
fun isCouple(): Boolean {
...
}
}
當老闆看完老過和阿對的程式碼後
再次興奮地提出新需求時
老過頓時暈了過去......
比賽真是太焦灼了
最後贏得獎勵的是?
第三個參賽者
老闆的傻兒子
他完全不會寫程式
但他使用 Ctrl+C,Ctrl+V
拷貝了阿對的程式碼
三、面試常見考點
在面試中,面向物件的常見考察點是三個基本特徵:封裝、繼承、多型。
- 封裝 封裝,也就是把客觀事物封裝成抽象的類,並且類可以把自己的資料和方法只讓可信的類或者物件操作,對不可信的進行資訊隱藏。
- 繼承 繼承是指這樣一種能力:它可以使用現有類的所有功能,並在無需重新編寫原來類的情況下對這些功能進行擴充套件。通過繼承建立的新類稱為「子類」或「派生類」,被繼承的類稱為「基類」、「父類」或「超類」。 要實現繼承,可以通過 繼承和組合 來實現。
- 多型性 多型性是允許你將父物件設定成為和一個或更多的他的子物件相等的技術,賦值之後,父物件就可以根據當前賦值給它的子物件的特性以不同的方式運作。簡單說就是一句話:允許將子類型別的指標賦值給父類型別的指標。 實現多型,有兩種方式,覆蓋和過載。兩者的區別在於:覆蓋在執行時決定,過載是在編譯時決定。並且覆蓋和過載的機制不同。例如在 Java 中,過載方法的簽名必須不同於原先方法的,但對於覆蓋簽名必須相同。
我對面向物件的理解:面向物件的程式設計方式使得每一個類都只做一件事。面向過程會讓一個類越來越全能,就像一個管家一樣做了所有的事。而面向物件像是僱傭了一群職員,每個人做一件小事,各司其職,最終合作共贏!
四、引申閱讀
最後,我們談談面向物件有什麼好處?
《大話設計模式》中大鳥給小菜講的故事非常經典:
“話說三國時期,曹操帶領百萬大軍攻打東吳,大軍在長江赤壁駐紮,軍船連成一片,眼看就要滅掉東吳,統一天下,曹操大悅,於是大宴眾文武,在酒席間,曹操詩性大發,不覺吟道:‘喝酒唱歌,人生真爽……’眾文武齊呼:‘丞相好詩!’於是一臣子速命印刷工匠刻版印刷,以便流傳天下。”
“樣張出來給曹操一看,曹操感覺不妥,說道:‘喝與唱,此話過俗,應改為‘對酒當歌’較好!’於是此臣就命工匠重新來過。工匠眼看連夜刻版之工,徹底白費,心中叫苦不迭。只得照辦。”
“樣張再次出來請曹操過目,曹操細細一品,覺得還是不好,說:‘人生真爽‘太過直接,應改問語才夠意境,因此應改為‘對酒當歌,人生幾何……’當臣子轉告工匠之時,工匠暈倒……”
大鳥:“小菜你說,這裡面問題出在哪裡?”
小菜:“是不是因為三國時期活字印刷還未發明,所以要改字的時候,就必須要整個刻板全部重新刻。”
大鳥:“說得好!如果是有了活字印刷,則只需更改四個字就可,其餘工作都未白做。豈不妙哉。
一、要改,只需更改要改之字,此為可維護;
二、這些字並非用完這次就無用,完全可以在後來的印刷中重複使用,此乃可複用;
三、此詩若要加字,只需另刻字加入即可,這是可擴充套件;
四、字的排列其實可能是豎排可能是橫排,此時只需將活字移動就可做到滿足排列需求,此是靈活性好。”
“而在活字印刷術出現之前,上面的四種特性都無法滿足,要修改,必須重刻,要加字,必須重刻,要重新排列,必須重刻,印完這本書後,此版已無任何可再利用價值。”
小菜:“是的,小時候我一直奇怪,為何火藥、指南針、造紙術都是從無到有,從未知到發現的偉大發明,而活字印刷僅僅是從刻版印刷到活字印刷的一次技術上的進步,為何不是評印刷術為四大發明之一呢?原來活字印刷是思想的成功,面向物件的勝利。”
作者:力扣(LeetCode)
連結:https://www.zhihu.com/question/31021366/answer/761614647
來源:知乎
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。