面向对象先导课程——PART3
內功與輕功
時隔半年回顧面向對象先導課程,有一些細節記不太清了,但溫故知新,在這里發表一些微小的心得體會。
課程內容
課程的主體內容整理如下:
| 0 | 課堂測試 |
| 1 | 重新認識代碼風格 |
| 2 | 字符串處理 |
| 3 | 快速debug |
其中課堂測試與之前的幾次課程差別不大,而主體內容真的讓人耳目一新,我將其總結為內功和輕功:所謂內功即在眾多細微隱藏巨大的能量,簡單的一掌就將人打得七葷八素;而輕功就是天下武功,為快不破。下文將分模塊進行討論。
代碼風格
代碼風格在我看來就是內功的一種。它說起來非常抽象,似乎是玄學,玄之又玄,而且只說風格也可能會是相當枯燥,所以我決定從實例入手,來闡述我對代碼風格的理解。
這里給出兩段代碼,兩段代碼的功能都是段判斷傳入的StringSet(片段1中為stringSet)類型對象是否為當前對象的子集。
- 代碼片段1
- 代碼片段2
哪一段代碼看上去更友好一些呢?
第一個代碼片段摘自我的2017年先導課第二次代碼作業,而第二段代碼是我現在重新理解后功能后重構的。當然我個人認為第二段代碼更友好一些。
實際上,我在重讀第一段代碼時都遇到了不少讓自己費解的地方,列舉如下:
| 3 | public int issubset | 一開始:“這個詞我好像看不太懂”;之后:“這個類干什么來著?”;“哦。。。”;最后:“反面教材就是它了。” |
| 4 | a.getlen() | 感覺可能知道這個方法的作用,但是在Java眾多的庫里都沒見過這樣的方法 ,總覺得不放心。 |
| 9 | for(int i...) | 應該是想做個遍歷,但里面的getByIndex看上去不像隨機訪問接口,需要寫著么多東西嗎? |
| 10 | stringSet.indexOf | 這怎么調用了個類的靜態方法?不對類的命名和類中元素的命名重復了。 |
| 17 | return 1 | 整個方法看下來,基本就是做個真偽判斷了,為什么要像C一樣返回1或0呢? |
我自己都看不明白,讓別人看可能就更難了。如果要debug,還需要花費不小的學習成本,簡言之,可維護性差。Java語言還是自由的,自由意味創造力,但也意味著有出錯風險。怎么權衡兩者,就是編程風格要解決的問題了。
回到例子,針對以上問題給出如下建議:
命名
命名做到能“望文生意”,用一套自己習慣的方式對不同類型的元素進行不同格式的命名。Java開發常用駝峰命名法,具體規則參考鏈接:命名。
這里再添加一些我現在使用的規則:
| 0 | 不混用中文拼音與英文,除了一些國際通用的名稱,正例如:Beijing。 |
| 1 | 不隨便縮寫單詞,反例如:Button縮寫為BT。 |
| 2 | 不使用錯誤的單詞拼寫。 |
| 3 | 不以$作為開頭。 |
為什么需要有這樣的一套命名規則呢?
簡而言之,提升代碼可讀性。目前大部分的代碼維護工作還是由人來完成的,之所以使用高級語言的一個原因就是提升代碼的可讀性,進而從某種程度上降低代碼的維護難度,降低(大工程)開發難度。Java是典型的適用于大工程的語言,(考慮到OO之后的代碼量)我想還是不要辜負它“易讀”的特性。
上述問題中不易讀的情況有:
issubset(這里不是針對某些腳本語言)在第一眼看那上去時可能不太容易理解;
stringSet類的命名和類中元素的名稱重復就更容易造成誤解了。
讀尚且費勁,如果要debug就更讓人頭大了,維護2000+代碼再加上隨意的命名方式可能就是火葬場了。
結構
你要是不知道這個方法應該怎么寫,看看Java自帶庫是怎么做的。 ——神.昂
上述問題中,getlen的設計就與官方庫中約定俗成的習慣不符。
如果在一個類中要一個從字符串解析出數據的方法,應該怎么設計這個方法呢?
如果是初學,可以看看Java官方庫怎么做的:
如果使用這樣的結構設計解析數據方法,可能比新建一個對象然后用parse方法對其賦值返回boolean要更易讀。使用try和catch的一個主要原因是避免維護結構正確性的代碼與實現主要業務邏輯的代碼混雜在一起。
這里舉一對例子:
- 代碼片段3
- 代碼片段4
兩段代碼都截取自parse類別的方法。
相比于代碼片段3,使用try和catch的代碼片段4,其主要工作一氣呵成,代碼也清晰簡單了很多。
需要指出的是代碼片段4也依然有提升空間,但在風格上會比代碼片段3要好一些。申明:代碼和第二次作業沒有關系。
LESS IS MORE
可能你會覺得很詫異,我居然會說開發Java也需要精簡。
這里須指出Java主要的復雜之處在于各種通過設計約束,降低代碼運行時自由度和后續開發自由度以提升穩定性,進而降低大型工程開發難度。
這并不是說用Java實現業務邏輯的代碼也會很復雜,其他語言精簡邏輯的思維方法在這里依然適用。但要注意一點不要輕易嘗試自己設計方法來完成標準庫已有方法可以完成的功能,簡言之,不要重復造輪子。
原因是:耗費時間;未必正確;得不償失,標準庫里的方法往往經過特殊優化。
上述問題中for(int i)來遍歷所有元素就顯得比較冗雜,且未必會快。(我也不容易知道getByIndex是不是隨機訪問。)
大廠開發手冊整理
如果你還沒有形成自己的風格,不妨看看大廠是怎么做的,也許能悟出些道理。——航航
Google Java開發手冊
Ali Java開發手冊
字符串處理
字符串處理之后會在拾遺中持續更新。
快速debug
課上要求的快速debug現在依然記憶猶新。
這一部分我認為是輕功。天下武功,為快不破,如何在限定時間之內找到所有的bug,實在是很不容易。
構建Bug樹
Bug樹是一個很好的辦法。
STEP1: 根據需求判斷程序的主要功能分支,先測試主要功能是否能夠正確執行。
STEP2: 如果發現有問題,調試定位找具體位置;
STEP3: 若沒有問題,嘗試往邊界條件試探,返回STEP2。
積累常識
- bug代碼1:
其中犯了常識性錯誤:clone要將對象在內存中復制一遍避免意料之外的共享,但執行上述代碼后this.box和a.box依然是引用了內存中的同一個對象。
- bug代碼2:
pattern用來構建正則表達式,除了匹配-和+還匹配|。注意要和(\\-|+)區別。
- bug代碼3.1:
當時課上我們需要在一份代碼中找出若干bug并修復。代碼的主要功能是從英文文本中截取單詞并保存在一個Set中(文本中的語義符號算作單詞一部分),以上是代碼截選。根據需求可知需要找出一種合適的分割單詞的方式,常規的想法是用“空格”和“換行”來分割單詞。以上代碼似乎就實現了這一功能。但要考慮到:Unicode編碼中有很多字符展現出來是所謂“空格”和“換行”的,以上代碼顯然沒有達到目的。
- bug代碼3.2:
接上一段代碼,這個方法作為保存String字符串對象的方法,功能是統計傳入的word在文本中出現了幾次。以上代碼依然不能滿足要求,原因是沒有考慮到word="test"而Set中有"test!"的情況。此時應該進一步明確單詞內匹配的模式(e.g. 貪婪模式)。
- bug代碼4:
閱讀代碼時很難區分是199711還是19971l。錯誤的理解可能導致意料之外的bug產生,應該使用19971L來表示。另:Java中不加L或l的數將被解析為int類型,超出范圍將報錯。
未完待續
寫在后面
這篇隨筆完全是拋磚引玉,希望有更多的同學發表自己的想法見解!
謝謝助教學姐提供的幫助!
之后將更新接口部分的內容,暫時先這樣。
轉載于:https://www.cnblogs.com/neolinsu/p/8602743.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的面向对象先导课程——PART3的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux协议栈(7)——网络层实现
- 下一篇: 微信小程序项目文件配置介绍