本文是我博客文章的備份。
這篇博文的主題是什麼?html
介紹了和麪向對象相關的基礎概念。本文的定位是隻解釋概念,所以看起來有點務虛。但依筆者拙見,基礎概念是極其重要的,如同一座樓房的地基,若對基礎概念的理解有誤差,對高層建築的理解也不會好到哪裏去,如同浮沙之上建高樓。java
在現實世界中,科學哲學(以波普爾爲表明)指導科學,做爲科學的物理學指導機械學,機械學指導製造出錘子,而後出現了錘子使用手冊。類比之,在程序世界中,某種抽象的理念指導着計算機科學,計算機科學指導着軟件工程,軟件過程指導製造出spring框架,而後出現了spring框架的使用文檔。我始終認爲,一名開發者要想對專業知識有着深刻的理解,則必需要沿着這條鏈往上走。python
注:本文是初版,匯聚了我對這一塊的系統理解,會隨着個人知識、經驗增加不斷更新。
<!-- more -->git
OOP屬於一種編程範式,因此它自己是一種設計思想。擁有OO特性則爲面向對象編程語言,語法上通常會提供類、封裝、繼承等語法。因此,OOP其實涉及到兩個概念,一個是OOP設計思想,它是程序員理解程序、設計程序的一種思惟方式;另一種是OOP語言,它提供語法支持來幫助程序員方便的用程序代碼去表達、實現OOP的這種設計思想。總結一下OOP的幾個重要部分:程序員
類:將數據和操做數據的方法打包在一塊兒做爲一個總體,叫作類。類中的數據叫成員變量、屬性,類中的操做叫成員函數、方法。類只是定義一種結構,咱們建立的類的實例纔是真正被分配有內存空間、存儲數據的東西。不一樣的實例包含不一樣的數據,因此調用相同的方法,這些方法對不一樣的類內數據操做,也許會獲得不一樣的結果。github
封裝:將一部分紅員變量、函數做爲內部實現隱藏起來(private、protected),而另一部分紅員變量、函數則對外公開(public)。算法
繼承:從一個現有的類派生出新的類,派生類自動擁有基類的部分紅員(public、protected),還可以添加本身的成員甚至是修改基類的方法。spring
多態:派生類可以修改(Override)基類的方法,當基類指針或引用指向派生類時,實際調用的是派生類的方法。若基類指針指向不一樣的派生類對象,則會表現出不一樣的行爲,這就是多態。數據庫
其實程序設計領域出現過不少編程範式,最著名的三種就是面向過程編程、面向對象編程、函數式編程。這引發了個人兩個思考。編程
第一,爲何會出現這麼多編程範式?我的拙見,之因此出現這麼多編程範式,是由於任何一個單一範式是沒法解決全部問題的。一種範式的發明有着特定的問題背景,也適用於必定的適用範圍。好比面向對象編程一開始就是爲了解決GUI編程的問題被髮明的,因此面向對象是最適合編寫圖形界面的。
在我初學面向對象時覺得任何問題都可以用它優雅的抽象,後來在接觸了其它的編程範式後,才發覺本身之前的思路太狹窄。技術沒有銀彈。
第二,爲何如今最流行面向對象編程?我的拙見,面向對象一來是容易從業務實體的角度進行抽象,這種抽象易於理解;二來面向對象中的接口給團隊合做帶來一種規約,封裝界定了團隊合做中每一個人的職責範圍,因此面向對象用於大型項目中很容易多人協做。
從初學程序設計的第一天起,老師、教材就會告訴咱們一句話:
算法 + 數據結構 = 程序
這是Pascal之父Niklaus Wirth提出了,這句話讓他得到了圖靈獎。(注:wiki上沒看到圖靈獎的資訊,但中文世界的各博客上都這麼說)將這句話衍生一下,任何一個程序,均可以拆分紅一個【運算部分】和一個【存儲部分】。
若是從硬件的角度來看待,運算部分就是在CPU中執行的指令,存儲部分便是在內存中保存的數據。在IT飛速發展的今天,互聯網公司大量運用的分佈式技術中也有這一理念的影子。當從物理部署的角度上看待分佈式系統時,咱們會將分佈式系統分爲無狀態的運算集羣(如java編寫程序啓動的進程),和專門存儲數據的存儲集羣(如Redis
集羣,MySQL
集羣)。即便是這幾年炒得比較火熱的區塊鏈技術,也能看到這一理念的影子。在可編程的區塊鏈ETH
上,某種角度上也能夠分爲兩個部分,第一個部分是做爲運算部分的智能合約,第二個部分是做爲存儲部分的鏈上數據。
這麼多年過去,Pascal之父的這句話對現代的各類程序仍然適用,這證實這句話的確揭示了有關程序的普世特色。
首先出現的面向過程編程,其程序觀主要有兩點:
static
上,有的分配在堆中等。不少時候有一種誤解,認爲過程式語言沒有封裝。雖然封裝是面向對象中的特色,但不表明面向過程當中沒有封裝。好比說做爲過程式語言的C語言,有對代碼的封裝機制,就是C語言中的函數,也有對也有對數據的封裝機制,就是struct
結構體,它可以將一組數據打包成一個總體以方便使用。
上面說的面向過程程序觀的兩個特色,稍微一琢磨,就發現其實在面向對象中也存在。所以,我的拙見,面向過程和麪向對象不是互斥的東西,面向對象是在面向過程的基礎上作了些衍生。體如今如下方面:
1、面向過程程序中,邏輯上程序分爲數據部分和運算部分,實際的源文件中數據部分和代碼部分倒是分離的、割裂的。咱們知道,有不少代碼是對數據的緊密關聯的操做 、變換,將這緊密關聯的兩個東西若是在源文件裏的距離比較遠,勢必會影響程序的內聚性。
面向對象編程就作了改進,設計出叫作「類」、「對象」的東西,把面向過程的程序裏的數據與和其緊密關聯的函數打包到一個實體中去,並將它命名爲類。按照這個思路,對於C語言裏的struct
結構體,若是把和結構體裏面數據密切相關的函數以函數指針的方式一塊兒打包到這個struct
中去,就有點OOP中類的內味兒了。
但在一個類的內部,咱們仍是要區分數據部分和運算部分,前者叫屬性,後者叫方法。
2、面向對象中一塊邏輯的具體實現,也是須要遵循面向過程當中「自頂向下,逐步求精」的理念。不然,就會看到這樣的代碼,在類的層次劃分得很優雅,但打開類裏面的函數實現一看,全部的代碼堆積在一個函數中,臃腫而不堪。所以,對於開發者來講,掌握面向過程編程是掌握面向對象編程的基礎。
3、雖然面向過程當中也有抽象和封裝,但僅侷限於函數層次,而面向對象可以在類的層次進行抽象。首先,對象給類型、數據訪問添加了一層間接抽象,一種數據的內部表示形式能夠有多種,一種類型也能夠有多種不一樣的實現,類層次的抽象就能將實際使用的那種隱藏掉。
不過,在《Code Complete》中提到一種稱爲ADT抽象數據類型的概念,擁有面向對象中類封裝特性的一部分。面向過程語言也能表達出ADT的概念。
4、面向對象編程中增長了繼承和多態機制,這是面向對象所特有的。恰當利用這兩種機制,可以對實際問題寫出更簡短、更清晰的代碼。
看過一些資料,不少人將封裝理解爲只暴露出提供給外部使用的接口,將其它信息隱藏以來。我的拙見,這個理解只解釋了封裝概念的一方面。
在書籍《Code Complete》中,解釋「封裝」的時候,還提到了相關的其它兩個概念,抽象和信息隱藏。所以,抽象、封裝、信息隱藏是三個密切相關的概念,互相交織,不該該割裂開來看待,也不該該將這三者混淆。這裏總結下《Code Complete》中對這幾個概念的解釋:
抽象:從更高的層次、以一種更簡化的方式來看待複雜的事物。例如房子,遠看是一棟建築物,近看是門、窗口、傢俱等組件的集合體,再拿放大鏡看發現都是鐵、木頭、油漆等各類材料,最後拿顯微鏡看發現都是分子。相似的,一樣一片內存的組織體,遠看它是一個List<T>
,能存順序元素的容器,近看發現它是指針、小塊內存、數據指針等元素的集合體。
從List<T>
的角度將這片指針、小塊內存等的複雜聚合體當作一個簡單的能存數據的容器,能大大下降心智負擔。這就是抽象。
封裝:標識出容許外部使用的成員函數、數據,區分出內部和外部,調用者只能使用外部的東西,不能看見內部的東西。爲何要區份內外?由於封裝是爲抽象服務的。
信息隱藏:信息隱藏大都有具體的語言機制(如java的private關鍵字)或規範約定(如python約定下劃線開頭的方法是非public的)支撐。信息隱藏將被標識爲內部的成員函數、數據隱藏起來。因此信息隱藏是爲封裝服務的。
有如下好處:
想要知道什麼是良好的封裝,就要先知道什麼不是良好的封裝。因爲抽象、封裝、信息隱藏這三個概念密切聯繫、互相交織,因此一個很常見的誤解是將信息隱藏等同於封裝。按照這種思路,在設計一個類的時候,將須要給別人調用的設計爲public
,而想讓別人知道、可能修改的函數、數據設計爲private
。
本科時我剛入門面向對象設計後一段時間就是持有這種粗淺的觀念編寫程序,但在實際編寫程序後總感受處處處不順手。其實,這種思路最大的問題是沒有結合真正的需求去思考,到底哪些是該隱藏的,哪些是不應隱藏的?信息隱藏只是一個戰術操做,而結合需求進行深刻分析的抽象纔是核心的戰略問題。借用《Code Complete》中的一句話:
不懂ADT的程序員開發出來的類只是名義上的類而已----實際上這種」類「只不過是把稍微有點兒關係的數據和子程序堆在一塊兒而已。
假設沒把需求考慮全面透徹就開始着手寫一頓噼裏啪啦敲打private void xxxx()
,到時候發現寫出來的類知足不了功能、或者發生了以前沒有意料到的需求變動,你的調用者就發現用你的public
接口根本無法實現需求啊,怎麼辦啊,他就只能一邊吐槽着類的編寫者一邊用一些奇葩怪異的hack方式來訪問被隱藏起來的細節。這些hack代碼,難以理解維護,稍不注意就容易出現詭異的bug與災難性的後果。
到頭來,你仍是要把這個private
改爲public
。想要真正杜絕外部調用者對內部細節的訪問,就必需要作到功能完備。
良好的封裝,有以下特性:
緊扣實際問題建模與功能完備性,是良好封裝的兩個必要條件。至於「最小」的原則,則是奧卡姆剃刀「如無必要,勿增實體」理念的體現。
思考:在實際的開發體驗中,功能完備是較容易作到的,但「最小」原則就比較難了,在本科學習C++的時候不少經典類的實現都能體現」完備且最小「原則帶來的美感,但後來發如今java中不少相似乎並不關注該原則,提供的方法也有大量冗餘。也許咱們能夠換個角度,將實際的類設計看做一個「完備且最小」的核心子集加上一組拓展函數的衍生。拓展函數的存在是爲了更方便開發者調用。
」最小「原則雖然容易被忽視,但它的重要性卻非同尋常。咱們用逆向思惟來看待這個問題,以java的流API爲例,表明流源頭的輸入流有文件流(FileInputStream
)、網絡流(SocketInputStream
)、存儲於內存的流(ByteArrayInputStream
)這3種,還有一個流裝飾器BufferedInputStream
。
若是讓一個沒有學習過程序設計知識的人來設計,它可能會設計出3種不帶Buffered特性的類,而後再設計出帶3種Buffered特性的類,一共6個類。可是若是遵循」最小「原則進行深刻思考,會發現這個Buffered
特性在與其它特性相正交的,將它單獨抽取出來,設計複雜度就馬上從乘法(3 x 2)變成加法(3 + 1)。
疑問: 正交性從直覺上彷佛應該是程序設計中有力的一大思惟工具,但我涉獵較淺,目前沒找到哪裏有資料詳細分析它。
繼承是指從一個現有的類型(基類型)派生出新的類型,新的類型自動擁有基類型的全部成員,而且也可以添加新的成員。在全部使用基類型的地方,均可以用派生類型去代替。
這是標準的解釋。但若是細細深究起來,這個代替怎麼理解呢?
思考:本科時初學面向對象時,第一次讀到「代替」這個詞,覺得是把用到基類型的代碼,Ctrl-c Ctrl-v替換成派生類型。往下讀下去看到進一步解釋時聯想起本身以前的猜想不由笑了。
咱們試着看一段代碼:
public static int getSize(List<T> list) {
return list.size();
}
public static void main(String[] args) {
ArrayList<> list = new ArrayList<>();
getSize(list);
}
getSize
函數的參數是List
,在程序員編寫這段程序的時候,對該參數瞭解到的信息只知它是一個List
,這是它的靜態類型。當這段程序運行起來後,這個list
參數被傳參一個ArrayList
對象,ArrayList
是它的實際類型。靜態類型是代碼裏編寫的類型,在編譯期間就能肯定,而實際類型是通過程序運行到該代碼處才能肯定的,通常要到運行期間才能肯定。
因此,「代替」的內涵指的是,全部靜態類型爲基類型的代碼處,在運行的時均可以指向其實是派生類型的對象。
至於多態,它指的是,若是新類型想修改掉基類型中方法的實現(Override
),看似長得同樣的代碼(如上面的getSize
函數),運行起來後,前後被執行兩次且傳遞了不一樣派生類型的對象,那這兩次實際上會執行兩個不一樣版本的代碼。
在《Effective C++》中將繼承分爲三種,這三種繼承看似類似,但其內涵卻截然不同。以下:
Override
父類的方法,java中只能繼承一個類的實現。本文稱爲實現繼承。final
關鍵字修飾或者純粹程序員自覺遵照規範),僅僅是本身添加新的方法。這種方式因爲沒有Override
,所以不涉及到多態。本文稱爲擴展繼承。下面分別討論。
1)接口繼承的好處。
主流的面嚮對象語言,都容許類繼承多個接口並鼓勵這種作法。咱們知道,在生活中,一個事物,根據實際所處的背景,會有很少的屬性劃分和標籤訂義。舉個例子,一個17歲的少年,在學校的身份是學生。對老師而言,他面對一個班級的包括少年在內的形形色色的人,他無需關心這些人在其它方面的差別,只須要關注他們做爲學生身份的這一個方面便可;去醫院,這個少年的身份是病人,對醫生而言,他面向病房裏的一堆人,根本無需關心他們的民族、宗教信仰、文化背景這些方面,只須要統一的關心他們做爲病人身份的這一個方面便可。
以java中的LinkedList
爲例,它實現List
接口,說明它的一個身份是一個能夠順序容納元素的容器;它還實現了Queue
接口,說明它的身份是一個有頭有尾的隊列。人在生活中有多個身份,LinkedList
在程序世界裏也有。
下面是Collections
中的一個函數,交換List
中的兩個元素。程序中有不少種徹底不一樣的對象,有的是ArrayList
有的是LinkedList
甚至還有第三方庫裏定義的對象,但沒有關係,只要他們實現了List
接口,swap
函數的開發者就不須要關注他們的差別性,而只須要關注他們做爲List
這一投影面的共性。
public static void swap(List<?> list, int i, int j) {
/* ... */
}
LinkedList
還實現了Queue
接口,因此,這個LinkedList
還能夠和其它的各類各樣的Queue
對象放在一塊兒,在程序的某個地方,被一段代碼不加區分的進行處理。
所以,接口繼承的優勢是,可以一視同仁的處理實現了特定接口的全部對象,開發者只需關注實際對象的一個方面而不須要關注全部方面,就可以大大下降程序的複雜度與開發者的心智負擔。
2)實現繼承的好處。
java語言中只支持繼承一個基類型的實現,C++支持多重繼承但該特性常常被批評。即便是單繼承,也常常在有些資料中聽到「慎重使用繼承,儘可能用組合來替代」。
這是由於,不少人將實現繼承做爲代碼複用的手段,但這會致使子類與基類出現強耦合。若是父類的邏輯被修改,那麼全部的子類都會被影響。以前提到,封裝的一個好處就是解耦、隔離變化,這樣一來,繼承如果有得不恰當則會破壞封裝。
若是僅是複用代碼,低耦合的代碼複用的方式有不少,何須用繼承這種危險的方式呢?在實際的編程中,我經常使用的方法有兩種:
Utils
類,將其做爲靜態的工具方法加進去。有時候剛好這個工具方法足夠通用,我就考慮將它移動到公用的Utils
工具類中。思考:在我初學面向對象編程時,我覺得java中的class
繼承特性是很重要的特性,而interface
彷佛沒什麼卵用。當真正在閱讀了優秀資料和書籍,通過本身的編程經驗淬鍊後,我如今的觀點徹底反過來了。若是把java中
class
的繼承特性去掉,我仍是可以寫代碼只不過要繞一點;可是若是把java中的interface
特性去掉,嗯,那這代碼就徹底無法寫了。
能夠想象一下,若是將java中class
的繼承特性去掉,class
之間不能相互繼承,考慮下jdk
的集合模塊會怎麼寫?
我的拙見,實現繼承的最佳方法,是將其當成能夠寫方法默認實現的【接口繼承】使用,該繼承的維度可標識該類的主分類緯度(由於只能繼承一個父類)。
子類若比較懶,可直接拿父類的默認實現來用,子類若以爲默認實現不能知足要求,就將其Override
可是不用super
關鍵字調用父類版本。這樣父類和子類之間的耦合就不太大。
思考:java中的List
接口,會搭配一個AbstractList
抽象類來使用。這個抽象類的設計初衷其實就是提供部分方法的默認實現,來方便使用者。
3)擴展繼承的好處。
擴展繼承不改變父類的行爲,僅僅是額外添加一些本身的數據和方法。咱們也分兩種狀況分析:
1、僅額外添加本身的方法。若是這些方法須要訪問protected
的成員,那這個方法和父類的耦合就比較緊密;若是這些方法僅需訪問public
成員,那其實能夠有替代方案,將這個方法放入Utils
中做爲靜態的輔助函數。做爲輔助函數會更通用一些,若是你要對一個現成的父類對象執行該操做,這時使用擴展繼承是沒用的,但輔助函數卻能派上用場。
2、額外添加本身的數據(成員變量)。因爲要額外保存數據,因此這種需求用擴展繼承實現最方便。
思考:能夠試想,假如不用擴展繼承,這種需求的實現,只能將這些額外數據和操做封裝爲一個輔助對象,並設計一個
HashMap
以該對象的某個獨特id爲key,以這個對應的輔助對象爲value,且要處理好生命週期的問題,若是主對象生命週期結束輔助對象也要跟着回收。這在C++裏可用析構函數作到,但在java裏基本上沒有一個靠譜的方案。
所以,擴展繼承總得來講,最有價值的仍是上面的第二種需求。
這裏對這5大原則作個總結。這五大原則分別是:
首字母
指代
概念
S
單一功能原則
對象應該僅具備一種單一功能
O
開閉原則
程序對擴展開放,對修改封閉
L
里氏替換原則
程序中子類對象可替代基類對象而不改變程序正確性
I
接口隔離原則
多個功能單一而小巧的接口,好於一個泛而大的接口
D
依賴反轉原則
依賴於抽象而不是一個實例
單一功能原則就是指的每種對象只作一件事情。咱們知道另一個更基本的原則是「高內聚、低耦合」,單一功能原則是基於「高內聚」的理念的。
開閉原則的幾個常見場景:1、設想你編寫了一個第三方的庫,並有擴展功能。庫的調用者若是要擴展庫的功能,他又沒法直接去修改庫的源碼。因此庫的擴展特性必需要遵循開閉原則。
2、一個大的項目由好幾我的分工合做編寫。你編寫的模塊供你的同事調用。若是你的同事想使用你的模塊卻感到礙手礙腳,不能經過public的接口完成,還必須去修改應該由你負責的代碼,這是不合適的。
里氏替換原則是對子類型的特別定義,派生類(子類)對象能夠在程序中代替其基類(超類)對象,不改變程序正確性。這個原則指導繼承的正確運用。
思考:這說明面向對象中的繼承,子類是(is)父類,這個「是」和平常生活中的含義根本不同,有些繼承設計雖然知足生活中的概念定義(子類內涵大於父類內涵,子類外延屬於父類外延),但不必定知足里氏替換原則。典型的就如那個著名的例子(正方形應該繼承自長方形嗎?)。
接口隔離原則指明客戶(client)應該不依賴於它不使用的方法。根據該原則,一個大接口,拆分紅更小、更具體的接口更好。
在實際的開發中,對這一點也有着不少體會。常常我須要實現spring中的某個擴展接口來知足一些需求時,會發現這些擴展接口包含了多個方法,我只須要使用其中的一個,但卻不得不被迫實現其它的幾個我根本用不到的方法。根據接口隔離原則,spring若是將這些方法拆分到不一樣的更小接口中,是更好的方法。
思考:但在實際開發中,將接口拆分得太細會引來過多的麻煩和複雜度。在實際編程中經常會見到另一種解決以上問題的方法,對於包含多個方法、且客戶通常不會使用全部方法的接口,會提供一個抽象基類,基類對每一個方法提供了默認實現。用戶使用時只須要繼承抽象基類並Override本身須要的方法便可。甚至在jdk8中,抽象基類都不須要了,定義接口方法的默認實現便可。固然,必須認可,這種方案本質上仍是沒有消除客戶對不使用方法的依賴,只不過讓客戶使用時更便利了。
在分層模塊中,樸素的作法中高層次的模塊會直接調用低層次的模塊,這樣高層就依賴於低層的實現細節。
依賴反轉原則是讓高層模塊先設計出能抽象底層模塊的接口,高層模塊的其它功能只依賴於該接口,而後低層模塊依賴於該接口。這樣依賴關係就被顛倒(反轉)。從高層依賴低層,變成低層依賴高層。
舉個例子:程序對數據庫使用的業務模塊是高層,各類數據庫如MySQL、SQL Server具體驅動實現是低層。咱們的業務模塊不依賴於具體的數據庫驅動,只依賴於抽象的jdbc接口。各個具體驅動實現抽象的jdbc接口。
設計模式是什麼?
設計模式是前人在軟件開發道路中摸索、總結、提煉而出的通用、有價值的設計方法、複用技巧。《Code Complete》中指出:
一個系統所外來的、古怪的東西越多,別人在第一次想要理解它的時候就越是頭疼。要儘可能用標準化的、經常使用的方法,讓整個系統給人一種熟悉的感受。一種特別有價值的標準化就是使用設計模式。
說的很清楚,設計模式是一種標準化的設計,也是一種習慣用法。就像建築工程中在設計圖紙時會參考前人的優秀、通用案例,設計模式就是前輩總結的、程序設計中的優秀、通用案例。
遵循設計模式,好處有如下幾點:
一來在設計程序時直接使用前者總結好的現成設計,不須要本身絞盡腦汁去思索,從這個角度說設計模式是一種思惟工具,利用好了能大大提升工做效率。
二來設計模式凝聚了前人的智慧和經驗,恰當的遵循設計模式可以避免不少本身沒法意料到的坑。
二來可以下降和同事的溝通成本,設計模式將某些有價值的設計標準化取一個名字,那麼在和別人溝通、討論的時候,咱們只須要使用這個名字,你們都能領會到該名字所表明的設計思路與智慧。
而若是是本身發明一套全新的設計,還得先花費很大的溝通成本和同事去解釋。
設計模式不是銀彈,不是萬能藥。
技術是爲需求服務的,是爲解決某個問題而針對性設計的。編程的過程,也是對具體問題抽象建模,將需求轉爲程序模型的過程。
因此,需求是什麼樣的,程序設計也要是是什麼樣的,程序設計要契合需求。若是對需求理解不透徹、不深刻,那必然作不出恰當精妙的程序設計。
若是把設計模式當銀彈,覺得只須要在程序裏使用了XX設計模式,就自動能解決掉XX問題,這種想法是對設計模式的嚴重誤解。設計模式僅僅是開發者思惟工具箱中的一種工具而已,但實際施工時是選擇用螺絲刀、釘錘仍是電鑽,則要根據當下的工做恰當選擇。
如前所說,設計模式是前人在軟件開發的實踐中總結出的標準化設計,假若脫離了該設計模式出現的代碼背景,那不管怎麼去理解不通透的。
摘錄一位大佬精彩的論述:
追根溯源以後,你會發現這知識最初的創造者通過了成百上千的錯誤。這就像愛迪生髮明燈泡,通過了幾千次失敗的實驗。知識的創造者把最後的成功記錄在文獻裏發表,而後你去讀它。你覺得獲得了最寶貴的財富,然而最寶貴的財富倒是看不見的。做者從那成百上千的失敗中獲得的經驗教訓,纔是最寶貴的。而歷來沒有人把失敗寫下來發表。沒有這些失敗的經驗,你就少了所謂「思路」,那你是不大可能從一個知識發展出新的知識的。
設計模式既然是從經驗中總結的,那麼它除了蘊含着對」正確實踐「的指導外,其實還隱含了大量被排除的」錯誤實踐「。沒有被」錯誤實踐」坑得抓耳撓腮的體驗,也就難以領會爲何「正確實踐」如此精妙。
所以,想要理解設計模式,就必需要親自寫一個大程序,由於沒有使用設計模式被搞得焦頭爛額,代碼被搞得混亂、臃腫一團糟。接着使用設計模式重構後發現代碼頓時變得清晰優雅了起來,這時才能深入領會到設計模式的精髓之處。
多時未更新博客,平時也只是在我的筆記裏即興寫一些心得,或者是收藏一些優秀大佬的精品文章到onenote中去。將這些零碎知識輸出成博客,實在太耗費時間和精力,出了學校越久越深感到時間精力的珍貴,處理完生活瑣事、陪陪女友,彷佛就到了睡覺的時間點了。
感謝最近一位大佬的鼓勵,讓我決定重拾這個良好的習慣。爲解決時間、精力的資源緊缺問題,我思考出以下方法:
(記於2020-01-10)。
1、書籍《Code Complete》
2、封裝和歸一化的概念
https://www.zhihu.com/question/20275578/answer/26577791
3、面向對象設計的五大原則SOLID