作者簡介:Zm,,愛生活,,愛分享。近五年的實際開發(fā)經(jīng)驗,,多個項目的積累與總結(jié),,在代碼中經(jīng)歷太多的喜怒哀樂。 本文來自作者 Zm 在 GitChat 上分享 「日常開發(fā)與設(shè)計模式的那點事」,。 【作者按】很多程序員不知道怎么組織代碼,、怎么提升效率、怎么提高代碼的可維護性,、可重用性,、可擴展性、靈活性,,寫出來的代碼一團糟,,但這樣一團糟的代碼居然能正常運行。 這樣的代碼經(jīng)歷,,你是否也似曾相識,? 身邊好多程序員都會有這樣的一個經(jīng)歷,過個一年半載再去查看曾經(jīng)寫下的代碼時,,很吃驚的在想,,這么糟糕的代碼,真的是我以前寫的嗎,?我居然能寫出這么糟糕的代碼,! 而對于還在維護的代碼,此時,,會萌生一種去重構(gòu)的想法,,或者會有一種更好的方式去實現(xiàn)。此時,,你與代碼的愛恨情仇已經(jīng)開始了…… 本篇主要從六大基本原則說起,,作為設(shè)計模式的引子,敘述六大基本原則和設(shè)計模式的關(guān)系,,后續(xù)會一篇一個設(shè)計模式,,詳細介紹設(shè)計模式與日常開發(fā),。 基本的規(guī)范和約束 對于基本的規(guī)范和約束,我相信每個合格的團隊都會有一套自己的玩意,,一方面統(tǒng)一標準,,增加可讀性和可維護性,另一方面也方便離職后出現(xiàn) Bug,后來者也能更快的去定位并解決問題。 雜亂無章的代碼實現(xiàn)一個大功能,,對于后來者去維護,無疑會親切的問候各路祖宗,。一個好的編碼習慣,屬于一個合格程序員的自我修養(yǎng),,于己于人,,百利而無一害。 對于開發(fā)中的規(guī)范和約束,,第一個要說的就是命名,。 這五年多的工作,和形形色色的人合作過,,記得最多的時候,,我曾同時期開發(fā)和維護五個項目,業(yè)余時間,,也曾和各路英雄好漢互相合作,、互相學(xué)習和共同進步,在這個過程中,,最讓我覺得頭疼的就是一些命名的不規(guī)范,。 不規(guī)范的樣式有很多,各種奇怪的命名都有,。我曾看到過這樣一串命名,,其中有兩個功能,一個叫做專欄詳情,,一個叫做專欄留言,,命名卻是 “ZhuanLaiDetalActivity” 和 “ZhuanLaiLiuYanActivity”,看得我很懵,。 這樣的命名,,就問你怕不怕!對于這樣的命名都怕了,,那真沒見過世面,,這個至少還能看出個大概,之前看到過一些漢語拼音的縮寫,,比如動檢證命名為 djz,,這個看起來才更懵了。 對于拼音命名,,這里說一點不知道會不會被噴,,遇到過一些朋友總是喜歡拼音命名,漢語拼音是中華民族推動漢文化的偉大創(chuàng)舉,,但是在編程的時候用拼音,,真的覺得好 low。 即使再牛逼的技術(shù)作支撐,,寫出來的代碼也像小學(xué)生的作品,,這里沒有看不起漢語拼音的意思,只是發(fā)表下內(nèi)心的一些想法,。 建議:大駝峰,、小駝峰或者下劃線命名都可以,如果沒有一個統(tǒng)一的標準,,可以參考《阿里巴巴 Java 開發(fā)手冊》,,對于剛?cè)胄械呐笥眩鼞?yīng)該從命名抓起,,對于以后的成長有很大的幫助,。 開發(fā)手冊下載地址:https://pan.baidu.com/s/1mjZxvSW。 再者要強調(diào)的就是注釋,。很多人也許覺得注釋是越多越好,,之前也在書上看到過提倡多加注釋,我覺得不然,,有些時候注釋給我們增加了很多負擔和誤解,。 上次 Review 的時候發(fā)現(xiàn),一些同事 Copy 我的一些代碼的時候,,其實是想做另一個功能,,只是想把一些代碼拷貝過去然后大修改(我不喜歡重復(fù)造輪子,對于相似的一些功能,,最好做的靈活一點,,提高代碼的可重用性和靈活性)。 其實可以重用的地方很少,,搞不明白為啥不自己寫那么幾行代碼,,這都不是事,讓我很懵逼的是他們把我的備注和作者也拷貝過去了,,當我進入那個類的時候,,發(fā)現(xiàn)作者是我,去 Git 查看歷史提交,,完全沒我啥事,,而且功能描述和此類完全不相關(guān)…… 對于注釋,,還有一點要說的就是一些多余的注釋,這個叫需要和命名相結(jié)合,,好的命名規(guī)范,,可以省略好多不必要注釋,比如 login,、register,,再加上登錄、注冊的注釋,,完全沒必要,。 良好的習慣,可以給我們開發(fā)帶來很多便捷,,但有些喜歡 textview1,、textview2 命名的,這些就算加了注釋,,等下文用到的時候,,看了也是一群羊駝在奔跑,上下奔騰的那種,。 for (int i = 0; i < j;="" i++)="" {="" todo=""> 對于這樣的代碼,,可能很多人會覺得很正常,也會有部分人會把責任歸咎于譚浩強老師,,是的,,譚老的書中問題確實很多,但這不是寫這種代碼的理由,。 日常開發(fā)中,,還有平時維護別人代碼的同時,總會去調(diào)試 for 語句,,難道不覺得這樣的代碼很糟糕,,看得有點懵嗎?就算加了注釋,,還是一坨一坨的,。 因為每個團隊有自己的規(guī)范和約束,大的公司,,會有一套自己的規(guī)范,,統(tǒng)一于各個團隊,不同的語言也有不同的約束,,如果日后有時間,,會專門寫一篇詳細的約束與規(guī)范的 Blog 贈送。 對于這塊,想寫的東西真的好多好多,,比如 case 后面的亂用,,1、2,、3 總是讓人費解,,比如必要的常量替代變量,比如線程池取代線程,,比如必要的地方使用單例,東西真的好多好多,,不再比如下去了,,今天就先說明兩條重點,命名和注釋,。 建議:合理使用注釋,,對于新手在學(xué)習期間,在陌生的代碼和不清晰的邏輯上,,盡可能多一點注釋,,便于理解,對于老鳥,,盡可能規(guī)范的命名,。 通過命名達到注釋的效果,但是對于邏輯復(fù)雜或者操作狀態(tài)太多的時候,,必要的注釋還是很重要的,,減小維護成本。 一些應(yīng)該熟知的編程思想 一個程序員用在寫程序上的時間大概占他的工作時間的 10-20%,,大部分的程序員每天大約能寫出 10-12 行的能進入最終的產(chǎn)品的代碼——不管他的技術(shù)水平有多高,。 好的程序員花去 90% 的時間在思考、研究和實驗,,來找出最優(yōu)方案,。差的程序員花去 90% 的時間在調(diào)試問題程序、盲目的修改程序,,期望某種寫法能可行,。 對于一個優(yōu)秀的程序員來說,邏輯才是最重要的,,他們愿意花更多的時間做思考,,這樣做的同時,就是更少 Bug 會出現(xiàn),,甚至可以把 Bug 率降到很低,。 我并不是很優(yōu)秀的開發(fā)者,但這些年依然有這么一個習慣,,對于復(fù)雜或者多樣的功能,,總會先理清思路,,先列舉出會有哪些操作,哪些地方是 Bug 的雷區(qū)應(yīng)該多注意,,我也經(jīng)常會和隊友提起,,一圖勝千言,理清思路再下手,,事半功倍,。 不管業(yè)務(wù)邏輯是否復(fù)雜,上去就是干,,發(fā)現(xiàn)有何不妥的再去修改,,發(fā)現(xiàn)漏掉的再去添加,這樣導(dǎo)致代碼總是一坨一坨的堆在那里,,經(jīng)過多次的修改,,已經(jīng)面目全非,對于維護的人來說,,更是苦不堪言,。 在此,筆者也建議讀者朋友,,不妨試一試先繪圖在動手,,把一個模塊繼續(xù)拆解成一個個接口,通過實現(xiàn)接口去實現(xiàn)這個模塊,,做到面向接口編程,,這樣可維護性會提升好多…… 對于模塊與模塊之間的通信,不應(yīng)該是類與類之間的關(guān)聯(lián),,而是通過抽象去實現(xiàn)交互,,抽象不應(yīng)該依賴于細節(jié),細節(jié)應(yīng)該依賴于抽象,,這話比較繞口,,說白了,就是面向接口編程,,而不是面向?qū)崿F(xiàn)編程,。 這樣做的好處就是,將來你要把這個被調(diào)用的類換成一個別的實現(xiàn)類時,,你就不用去把調(diào)用過它的類一個個改掉了,,因為它們調(diào)的是接口,接口沒變,,在配置里把接口的實現(xiàn)類換成新的類,,就全部都替換掉了。 類之間的耦合越弱,越有利于復(fù)用,,一個處在弱耦合的類被修改,,不會對有關(guān)系的類引起波及。 建議:理清思路再下手,,事半功倍,。開發(fā)過程中,不妨先定義好接口,,通過實現(xiàn)接口去完成模塊的開發(fā),,盡可能的減小 Bug 率,寫出更加優(yōu)質(zhì)的代碼,。 技術(shù)能力的提高,,從代碼上的體現(xiàn)主要在于 “高內(nèi)聚、低耦合”,,因為這些思想衍生出許多開發(fā)模式,比如現(xiàn)在比較流行的 MVC,、MVP,、MVVM 等。 版本迭代與重構(gòu) 我們在做任何系統(tǒng)的時候,,都不要指望系統(tǒng)一開始時需求確定,,就再也不會變化,這是不現(xiàn)實也不科學(xué)的想法,,而既然需求是一定會變化的,,那么如何在面對需求的變化時,設(shè)計軟件的可以相對容易修改,,不至于說,,新需求一來,就要把整個程序推倒重來,。 相信很多朋友都遇到過,,原本一個很普通的需求,在經(jīng)歷過 N 次迭代和修改后,,已經(jīng)形成一個龐大的功能,,隨著版本的不斷迭代,維護起來的成本也隨著越來越大,,這樣就形成了一種惡性循環(huán),,重構(gòu)代碼即將登上歷史舞臺。 不可否認,,從維護成本上看,,重構(gòu)確實是一個很不錯的方案,重構(gòu)的成本比原基礎(chǔ)維護的成本更小,也更方便以后的維護,。有些公司甚至在多次版本迭代后,,直接把整個項目推到重構(gòu),這樣的事情不僅僅發(fā)生在小公司,,在一些大公司,,也是會發(fā)生多次。 從技術(shù)上來說,,重構(gòu)復(fù)雜代碼時要做三件事:理解舊代碼,、分解舊代碼、構(gòu)建新代碼,。 而待重構(gòu)的舊代碼往往難以理解,,尤其是在多次迭代且多人經(jīng)手的模塊;模塊之間過度耦合導(dǎo)致牽一發(fā)而動全身,,不易控制影響范圍,;舊代碼不易測試導(dǎo)致無法保證新代碼的正確性,尤其是在產(chǎn)品文檔不全的時候,。 這是上次 Review 時候發(fā)現(xiàn)的一段代碼,,先不說常量使用的不規(guī)范,這是經(jīng)過一次次產(chǎn)品迭代后的結(jié)果,,但這不是借口,,造了這么多次輪子,真不應(yīng)該,。做為代碼可重用性的反面教材,,此處體現(xiàn)的淋漓盡致,如果有更多的狀態(tài),,此處必然還是會重復(fù)多次…… 建議:重構(gòu),,并不是萬能的,重構(gòu)后的代碼,,當再次經(jīng)歷后續(xù)幾個版本修改后,,代碼又顯的雜亂無章,那一坨坨代碼總是在不斷的重演,。 既然無法確定需求日后是否會修改,,那我們只能通過提高代碼質(zhì)量來應(yīng)對,以不變應(yīng)萬變,,合理設(shè)計接口,,每次更改需求時多思考,對于多次使用的代碼進行封裝提取,,盡可能少的改動既有的邏輯,。 設(shè)計模式的重要性 會建筑設(shè)計的是建筑工程師,,不會建筑設(shè)計的是搬磚的。 前面已經(jīng)說了很多,,現(xiàn)在直接說一下設(shè)計模式的重要性,,提到設(shè)計模式,就必須要提到六大基本原則和架構(gòu)設(shè)計,,提到架構(gòu)設(shè)計,,設(shè)計模式的重要性便可想而知。 首先,,六大基本原則還是有點爭議的,,我之前看到的書籍中,一般都是單一職責原則,、迪米特法則,、里氏替換原則、開閉原則,、依賴倒置原則和接口隔離原則,。 但我最近在一些帖子上看到,有一種說法是沒有接口隔離原則,,而是合成 / 聚合復(fù)用原則,,為了不影響之前的準備,合成 / 聚合復(fù)用原則會單獨拿出來說一下,。 六大基本原則,它是整個架構(gòu)設(shè)計的靈魂,,是架構(gòu)設(shè)計的一種指導(dǎo)思想,,而設(shè)計模式是架構(gòu)設(shè)計的一種具體設(shè)計技巧,是架構(gòu)設(shè)計的具體實踐,。 先從架構(gòu)設(shè)計說起,,對于架構(gòu)設(shè)計,主要體現(xiàn)在抽象能力,,抽象能力又依賴于架構(gòu)者編碼的閱歷,、功能的拆解和理解、邏輯的嚴密性,。 做架構(gòu)設(shè)計應(yīng)該盡可能且更全面的考慮問題,,盡可能做好代碼的包容性,海納百川,,有容乃大之勢,,這是架構(gòu)設(shè)計者應(yīng)該具備的基本條件。 考慮的問題越周全,,包容性越強,,則工作難度越大,,給自己造成的障礙也越多。合理的將這些細節(jié)問題進行抽象,,并提出解決方案,,抽象程度越高,解決方案越合理,,這才是架構(gòu)者的價值所在,。 從具體的需求,到代碼實現(xiàn),,再到具體的產(chǎn)品,。架構(gòu)設(shè)計的目的無外乎系統(tǒng)的復(fù)用性、擴展性與穩(wěn)定性,,具體的東西是無法很好地體現(xiàn)這些特性的,,只有抽象的事物才能最好的體現(xiàn)。 在架構(gòu)設(shè)計的過程中,,單一職責原則告訴我們應(yīng)該更好的體現(xiàn)高內(nèi)聚,、低耦合,這個類是用來數(shù)據(jù)請求的,,就別放一些解析 JSON 的方法,,如果這個類是用來圖片加載的,View 的注解請隔離開,,做到一個類只負責一個職責,,只有一個引起變化的原因。 如果一個類承擔的職責越多,,就等于把這些職責耦合到一起,,會帶來一些不必要的維護成本。從大的角度來說,,MVP 和 MVC 模式都是單一職責原則的體現(xiàn),,model-視圖-控制相隔離,各司其職,。 迪米特法則指導(dǎo)我們?nèi)绻麅蓚€類不必彼此直接通信,,那么這兩個類就不應(yīng)當發(fā)生直接的相互作用。如果其中一個類需要調(diào)用另一個類的某一個方法時,,可以通過第三者轉(zhuǎn)發(fā)這個調(diào)用,。 類之間的耦合越弱,就越有利于復(fù)用,,一個處在弱耦合的類被修改,,不會對有關(guān)系的類造成波及。主要是強調(diào)了類之間的松耦合,。 對于里氏替換原則,,或許很多人沒聽過這個名詞,,但是在實際開發(fā)過程中卻無時無刻不在使用,其實很簡單,,子類型必須能夠替換掉它們的父類型,。 舉個簡單的例子,“List< string=""> list = new ArrayList<>();”,,這么做的好處其實很簡單,,比如有一天 ArrayList 滿足不了需求,需要改用 LinkedList,,只需要把 ArrayList 替換成 LinkedList,,而不是把全局的 list 對象都改一遍,提高了可維護性,。 開閉原則是面向?qū)ο笤瓌t的核心,,有兩部分組成,對擴展開放,,對修改關(guān)閉,。軟件需求總是會變化的,對軟件設(shè)計人員來說,,必須在不需要對原有系統(tǒng)進行修改的情況下,,實現(xiàn)靈活的系統(tǒng)擴展。 對擴展開放,,就是對抽象編程,,而不是具體編程,因為抽象相對穩(wěn)定,,通過接口或者抽象類約束擴展,,對擴展進行邊界限定,不允許出現(xiàn)在接口或抽象類中不存在的 public 方法,。 讓類依賴于固定的抽象,所以,,對修改是關(guān)閉的,。這是建立在繼承和多態(tài)的基礎(chǔ)上,可以實現(xiàn)對抽象類的繼承,,通過覆蓋其方法來擴展方法,。 依賴倒置原則指的是依賴于抽象而不是依賴于具體實現(xiàn),這一塊在上述已經(jīng)說過,,其實就是面向接口編程而不是面向?qū)崿F(xiàn)編程,,這樣做的好處就是解決耦合。 一般情況下抽象的變化概率很小,,讓用戶程序依賴于抽象,,實現(xiàn)的細節(jié)也依賴于抽象,。即使實現(xiàn)細節(jié)不斷變動,只要抽象不變,,客戶程序就不需要變化,。這大大降低了客戶程序與實現(xiàn)細節(jié)的耦合度。 接口隔離原則認為,,“使用多個專門的接口總比使用單一的接口要好”,。 一個模塊應(yīng)該依賴它需要的接口,需要什么接口就提供什么接口,,把不需要的接口剔除掉,,同時也應(yīng)該遵循單一職責原則,這樣避免臃腫的接口帶來的污染,,將沒有關(guān)系的接口合并在一起,,形成臃腫的大接口,就是對接口的一種污染,。 接口的粒度也不能太小,,太小會導(dǎo)致接口額數(shù)量劇增,對開發(fā)人員不友好,;接口額粒度太大,,靈活性降低,無法提供定制服務(wù),,給整體項目帶來無法預(yù)估的風險,,合理的設(shè)計接口,也是一門藝術(shù),。 對于這張圖,,一定存在很多的爭議,因為很多設(shè)計模式都用到了多個基本原則,,上圖只是對設(shè)計模式的一個比較粗糙的總結(jié),。 強調(diào)六大基本原則 (含有合成 / 聚合復(fù)用原則) 在設(shè)計模式中的具體體現(xiàn),同時也說明了六大基本原則和 23 種設(shè)計模式是相輔相成的,,六大基本原則作為設(shè)計模式的基石和模板,,設(shè)計模式是六大基本原則運用的靈活體現(xiàn)。 合成 / 聚合復(fù)用原則這個是存在一定爭議的,。目前有的書中還是保留了合成 / 聚合復(fù)用原則去掉了接口隔離原則,,合成 / 聚合復(fù)用原則指的是少用繼承,多用合成關(guān)系來實現(xiàn),。 合成和聚合都是對象建模關(guān)聯(lián)關(guān)系的一種,,聚合表示一種弱的擁有關(guān)系,整體由部分組成,,部分可以脫離整體作為一個獨立的個體存在,,合成則是一種強的擁有關(guān)系,,體現(xiàn)了嚴格的部分和整體的關(guān)系,部分和整體的生命周期一致,,部分不能脫離整體,。 總結(jié) 六大基本原則是面向?qū)ο笏枷氲捏w現(xiàn),單一職責原則與接口隔離原則體現(xiàn)了封裝的思想,,開閉原則體現(xiàn)了對象的封裝與多態(tài),,而里氏替換原則是對對象繼承的規(guī)范。 至于依賴倒置原則,,則是多態(tài)與抽象思想的體現(xiàn),。在充分理解面向?qū)ο蟮幕A(chǔ)上,掌握基本的設(shè)計原則,,并且能夠在項目設(shè)計中靈活運用,,就能夠改善我們的代碼質(zhì)量和結(jié)構(gòu)設(shè)計。 尤其能夠保證可重用性,、可維護性,、可擴展性和靈活性,這也是理解和掌握設(shè)計模式必備的知識,。 補充 對于六大基本原則,,這是我們開發(fā)都應(yīng)該熟記于心并且靈活運用的,對于設(shè)計模式在日常開發(fā)中的運用,,有一點還是要強調(diào)的,,適合自己的才是最好的。 如果此時一個模塊是很輕量級,,僅僅為了使用設(shè)計模式而用設(shè)計模式,,這無疑也會顯得不倫不類,使項目變的臃腫,,同時也帶來一些不必要的維護成本(雖然維護成本很低),。 最近開始整理資料,準備寫設(shè)計模式專題,,主要是 MVP 爬坑與迪米特法則,、Framework 與開閉原則、單例與爬坑,、換膚與觀察者模式、加載列表與模板方法模式,、構(gòu)造函數(shù)與建造者的對比,、多個第三方登錄與命令模式,后續(xù)還會繼續(xù)完善,,敬請期待,。 |
|
來自: flyk0tcfb46p9f > 《待分類》