随笔感悟 — 函数封装
- 思緒來源:我在初學算法的時候,一直不愿意調用除了基礎函數以外的其他功能函數,特別是有些函數里面就只有一句代碼,這...有什么封裝的必要?(例子:數據結構順序表,其添加刪除肯定屬于基本邏輯操作,但這順序表的長度到底有什么封裝的必要,順序表誒,四舍五入等于數組,求個長度還封裝,哇,一句代碼的事情誒,有沒有搞錯(我就舉一個小例子
- 我愿意改變,但有誰能告訴我原因是什么好嗎,跪求啊
- 真的是感悟,都快上升到哲學問題了....寫的就是一種感覺
- 意識流
?
?
?
?
- 起因
 
我有認真思考過“應該如何去編程”,但是在學習的過程中我卻只能讀到一系列指示,像這樣命名變量(大小駝峰法、匈牙利命名法、下劃線法)、像這樣組織代碼(結構化,流程化)、像這樣封裝函數(高內聚低耦合)等等等等。
是,在大部分情形下,這些都是不錯的做法。但這樣做的理由是什么?
?
?
大家寫代碼都有自己的習慣偏好,對吧。雖然代碼個人風格太嚴重不一定是好事,因為別人可能看不懂這份代碼,由此可見這份代碼的可讀性和溝通性不見得好,這會帶來很多麻煩事。但是,這不代表我會在一無所知毫無辨別能力的情況下,就隨隨便便隨波逐流地采用大眾方法。
我知道很多事情不能一概而論,不同的情況下有著不同的最優解法,但這些做法到底在什么樣的上下代碼中才能成為“通法”卻沒人指出。就像最基本的變量命名,如果要準確表達出變量的職責而使用很長的名字,這會使代碼變得冗長從而加大閱讀難度,可是如果為了圖方便而使用縮寫命名,那效果會更加不好,因為這太容易產生歧義了,其名字不具有描述唯一性。那面對這種情況我們該如何抉擇呢?
其實,我在課本中從來沒有看見過這些答案。
?
?
?
- 溯源
 
回想當初,我也有這方面的壞習慣,在最初學習編程的一段不算太長的時間里,我每天樂滋滋地跟著課本去使用一套 abcdefjhijklmnopqrstuvwxyz 的命名覺得自此打遍天下無敵手......
后來發現不對勁兒了,因為在復習的時候我有點看不懂我以前寫的代碼了,這感覺可不好受,看個代碼跟做閱讀理解題一樣,說出去搞笑呢。于是我開始注意自己的代碼書寫規范,有去搜索過一些文章博客,也有瀏覽過一些公司發布的代碼規范手冊(如谷歌、BAT、華為),建議新手小白們有空也去看看學習學習。
但是看過以后呢,我覺得有些規范是說得很對,但為什么很對就不知道了。呵,學不到本質,這不是我想要的。不過不知道也沒關系,畢竟你才剛開始學習能意識到代碼書寫規范的重要性和保有良好的注釋習慣這就已經是個很好的開始了,但顯然我還是想知道原因。
?
?
我有思索過、有考慮過、也有糾結過,但有時候我得出的結論明顯并沒有我想象期望中的那般好。可那又怎樣,新手本來就很容易犯各種各樣的錯誤,如果你現在不花時間去思考,等到以后還不是會花時間思考,一樣的沒差。
只是令我感到咋舌的是,如果每次學習自己喜歡的新事物新規則時就直接拿來用了,那你可能會與這些規則背后所蘊含的斟酌與考量其產生的奧妙感覺擦肩而過。如果每次寫代碼都按著模版照搬,管它三七二十一的反正現成的代碼能用就是了,那這種復制粘貼簡直和代碼界的搬運工沒有任何區別。其實,此處涉及到造輪子和用輪子之間的權衡問題,關于這個的討論網上有很多,不妨搜搜看。
嗯,寫這么些話是希望大家能抓住每一個有感而發的思考瞬間,不要錯過與那些隱含知識碰面的機會。但值得注意的是,千萬不要刻意地去思考一件事,那是挑刺兒。不過,若實在思考不出來也沒關系,因為你與代碼打交道的時間還很長,可以不急于一時。
畢竟在后續成長實踐的過程中,你會逐漸有深刻認識的,就像學習軟件開發一樣,能慢慢地體會到軟件不是簡單的編寫程序,其包含了很多技術問題間的協調,如分析方法、設計方法、形式說明方法、版本標準等等。到那時候你會自然而然地為了解決問題而去思考的,所以別擔心,有機會的。
?
?
但對于我來說的話,思考這種情況還是很常見呢。我想著知其然還要知其所以然,就這樣深究下去了,沒錯,這遲早會上升到哲學問題——“如何編程?”
可是這種哲學問題真的很容易泛泛而談,即使大家給出了許多規則,但看到規則的大多數菜鳥們(包括我),其實根本不知道規則背后隱藏著什么樣的思索,也不知道什么時候該打破規則、突破束縛。嘶,這就很扎心了。
我當然意識到了代碼可讀性的寶貴,也知道要寫出高質量的代碼勢必要付出很多心血,融合自己與他人的代碼風格總是有許多深思熟慮的。畢竟優秀的代碼,絕非僅僅是功能的堆砌,它需要做到能有效的傳達信息。因為有無數個事實表明,修改代碼是編寫代碼成本的3倍、5倍之多,而且別人閱讀理解你代碼的時間可能要遠遠超出你編碼所用的時間。
所以,要學會利用代碼去精細準確的傳達出你的想法,學會用代碼與他人溝通,絕對不要寫出垃圾代碼。要知道,寫出簡潔明了結構良好且效率高的代碼是一件重要、困難并且有趣的事,它值得每一個人下功夫去研究。
但這種代碼不會自然產生,它是由數十個數百個看似瑣碎,實則無比重要的決定堆砌出來的。我們在創造高質量代碼的過程中,不斷地在做出一些微小卻重要的決定,雖然每一種編程方式都由許多決策組成,而且它們之間互相支持協同工作,但從中抽出一條卻不一定能有效果。比如,片面地追求最精簡最優化或最高效的代碼就是個誤區,代碼并不是行數越少就越好,如果仔細注意或觀察就會發現我們使用最多的代碼是那些邏輯清晰結構良好的代碼,而非那些標新立異看起來高深莫測的代碼,當然炫耀的除外。
歸根結底,這么說這么做的目的只有一個,就是希望你的代碼可以清晰的表達出你的意圖,并且自始至終保持一致性,讓其他人可以理解并信任你的代碼,能夠信心十足的修改它,這樣的代碼才是好代碼。
?
?
其實,代碼要寫得好,要點之一就是不能repeat東西。這是因為一旦要改需求,你會欲哭無淚的,程序員還是要減少修改代碼的痛苦。那么重點就來了,在學習數據結構的時候關于函數封裝的問題我可是吃過大虧,呵,接下來我將講述我的慘痛經歷。
不過在此之前,明確指出函數封裝是有必要的,先不論你的代碼風格或偏好習慣如何。
?
?
?
- 函數封裝
 
嗯...我印象最深的是當時用順序表完成字符串操作的那個代碼,雖然現在回看過去覺得很簡單,但是那個時候的我確實不明白為什么要把一些很基本的語句包裝起來。我一直覺得把求length的方法專門單獨寫進一個函數里面是如此的畫蛇添足和多此一舉,所以我理所當然的沒聽老師的方法也沒按照課本上提供的案例那樣來做,偏要自己寫。
但是你知道的,關于數組下標的計算一直都是一個很容易出錯的東西,所以我在后續手動修改順序表長度時遇到了很多挫折。首先,肉眼查錯和手動枚舉修改代碼是非常耗時耗力的傻行為,這不僅過程痛苦而且還容易出現紕漏,最重要的是這種修改過程會影響你理清思路,因為你的注意力全部集中在修改語句上,而非修改結構組織上。于是稍有不慎,改著改著就發現代碼結構亂了,這太糟糕了。
我呢,在修改完代碼后再次進行測試時,發現有個功能的結果出錯了,但找了半天沒覺得哪里出錯。畢竟思路什么的都是對的,而且我還自以為挺謹慎的,不可能出錯啊。直到我決定開始逐行排查代碼的邏輯,最后發現有一個函數的length++初始值不對,多加了個1。我的天吶,我當時簡直都要被氣笑了,錯在這個地方讓我找了近一個小時。
那個時候我就明白了,像求長度這種使用率較高的底層代碼,如果你把它封裝成了函數,那么在你需要修改它的時候就只用在函數里面修改一次即可,不用像我之前那樣手動一個個的去修改,找不全還很容易出錯。所以這種封裝的感覺啊,還挺像“一鍵修改”功能的,如同機器上的零部件一樣制造一次后批量使用。
?
?
是的,函數封裝相當于替換或減少了重復性的代碼,可以使程序精簡化。試想一下,當所有代碼都寫進一個main函數里面是不是很可怕,畢竟,一個函數實現多個功能會給開發、使用、維護帶來很大的困難。若將沒有關聯或者關聯很弱的語句放在同一函數中,會導致函數職責不明確,讓人難以理解、難以測試、難以改動,所以將重復性的代碼提煉成函數可以降低維護的成本。
但事情總有兩面性,如果掌握不到封裝的精髓,那很容易就犯下封裝過度的錯誤。其具體表現就是,不斷的分解再分解問題卻對于精簡邏輯結構毫無頭緒,導致寫了無數行代碼封裝了一堆函數但就是看不到一個完整的功能,分裂問題起來收不住,不知道何時適可而止。往往一個函數里面就三兩行代碼,互相之間調用這個調用那個的,引用得亂七八糟,最終把功能實現了但代碼的維護性也讓人不忍直視,自以為封裝細致,其實是拿捏不好這個度。
所以你就知道了,我為什么當時會對那個length長度的封裝抱有這么大的抵觸,因為封裝的函數里面就只有一行代碼,這真的讓人很難以接受啊。
?
?
那么,在編程時如何決定是否將代碼封裝成函數,或者如何辨別才不會做無所謂的封裝呢。我覺得還是要以代碼邏輯直觀為最優先原則,然后講究封裝的高內聚低耦合,剩下的其他原則均低于此,即使是代碼的時間和空間復雜度也不行。當然,還有幾個簡單易懂的封裝指標,可以借鑒一下:
?
?
畢竟,封裝的本質就是要減少重復信息的產生,歸根結底無非就是為了能更好的重構代碼,但這只是一部分原因,其實重復的本質并不僅僅局限于此。
重復,是由于你把同一個信息散播在了各個地方,這相當于復制了代碼。可是就算你把重復的代碼抽象成了函數,但函數調用了幾次,這實際上也等于重復了幾次。那么這時就應該從代碼的結構框架上改,也就是利用數據結構和算法的知識來解決重復問題。
其實,“去掉重復的信息”這個問題也很值得深思,因為就算我們撇開代碼的精簡度不談,就從你以后的工作來說,由于客戶的需求總是在變,如果每次你遇到類似問題時,經常就直接寫,而不是找曾經寫過的函數來擴展,那這樣解決問題的效率就高不起來。所以為什么不把你需要的各種信息提取出來,把專用函數擴展變成通用函數來減輕自己的負擔。
所以記住吧,如果信息一旦被重復,那你的代碼就會出現不同程度的腐爛和破窗,不要讓你的這份代碼散發出不好的氣息。
?
?
?
- 代碼重構
 
啊,當然了,凡事不能一概而論,這并不是什么代碼都能重構的,就算你想重構但有些代碼就是不能重構也沒法重構。
因為在一個具有一定規模的系統中,存在復雜模塊是無法避免的事情,一次性把這些復雜模塊都標注出來并不現實,而且我們對這些模塊進行調整和優化也存在風險。所以,以合適的策略去優化真正需要優化的部分是很重要的,不要局限于重構。
況且還有些東西根本就沒法重構,比如幻數。就像哈希表中的133,或者你自己在編寫代碼時寫的0x2123、0.0211f等東西,當時你是明白這個數字的意思,但是別的程序員看這個代碼可能很難理解,甚至過了一段時間之后,連你自己再看這段代碼也忘記了這個數字代表的含義。
總之,你不記得也不知道這個數字的具體含義究竟是代表著什么,或者這個數字根本就沒有什么含義,但是代碼編譯后程序依然可以正常運行,那這種東西就是幻數。舉一反三,就算有一段很莫名其妙的代碼擺在你面前,你不懂這段代碼有什么意義,但它就是能很好的運行程序,而且沒了它還不行。那這種東西怎么重構,它根本就不好重構,所以就別重構了。
對于這種現象呢,很多人覺得這種代碼是定時炸彈,不改掉它心里不痛快。可是,只有當我們真的需要關注并修改它時,它才是問題所在,如果沒有人需要閱讀或修改這段代碼,那么它的復雜度就算高成指數倍,那又有什么關系呢?但如果你實在犯了完美主義的強迫癥,那就去改吧,畢竟心里痛快比較重要。
?
?
?
- 結尾
 
所以你會發現,大家都說封裝好,但有些時候封裝代碼就不好,大家說重構很重要,但有時候重構根本就沒有意義。在一些特定的情況下,規則并不適用,那么當你學著了解規則的同時,也得想一想規則背后的原因,不然這跟封建迷信有什么區別。
最后,祝大家都能寫出好代碼,嗯。
?
?
?
?
?
?
總結
以上是生活随笔為你收集整理的随笔感悟 — 函数封装的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 鸿蒙是哪个人类文明,【图说鸿蒙】鸿蒙设定
 - 下一篇: 图说区块链 神一样的金融科技与未来社会