1.if層次過多。
舉一個很有趣的例子,假如世界上隻有兩種烤鴨,在北京的叫做北京烤鴨,不在北京的叫做非北京烤鴨,寫爛代碼的程式員思路如下:
如果烤鴨不在地球,它一定不是北京烤鴨;
如果烤鴨不在陸地,它一定不是北京烤鴨;
如果烤鴨不在亞洲,它一定不是北京烤鴨;
如果烤鴨不在中國,它一定不是北京烤鴨;
如果烤鴨不在北京,它一定不是北京烤鴨;
否則,這是北京烤鴨。
僞代碼如下:
if(!烤鴨在地球){
輸出非北京烤鴨。
}else{
if(!烤鴨在陸地){
輸出非北京烤鴨。
}else{
if(!烤鴨在亞洲){
輸出非北京烤鴨。
}else{
if(!烤鴨在亞洲){
輸出非北京烤鴨。
}else{
if(!烤鴨在中國){
輸出非北京烤鴨。
}else{
if(!烤鴨在北京){
輸出非北京烤鴨。
}else{
輸出北京烤鴨。
}
好把,如果真有哪個程式員是這樣寫代碼的,那麼我真的很想一巴掌把它拍到火星去。不要這樣嵌套好嗎?就算你真的是這麼想,也可以這樣寫:
if(!烤鴨在地球){
輸出非北京烤鴨。
}else if(!烤鴨在陸地){
輸出非北京烤鴨。
}else if(!烤鴨在亞洲){
輸出非北京烤鴨。
}else if(!烤鴨在中國){
輸出非北京烤鴨。
}else if(!烤鴨在北京){
輸出非北京烤鴨。
}else{
輸出北京烤鴨。
}
這個時候你可以發現原來前四個條件的内容都是相同的,那麼可以這樣寫:
if(!烤鴨在地球
||!烤鴨在陸地
||!烤鴨在亞洲
||!烤鴨在中國
||!烤鴨在北京){
輸出非北京烤鴨。
}else{
輸出北京烤鴨。
}
當你發現前面if的條件過分臃腫的時候,可以考慮反過來會不會更容易
最終變成:
if(烤鴨在北京){
輸出北京烤鴨。
}else{
輸出非北京烤鴨。
}
可見,邏輯簡化可彌補思維的不足。
這裡有兩個公式:
公式1:
if(條件1){
//做事情A
}else if(條件2){
//做事情A
可以轉換為:
if(條件1 || 條件2){
//做事情A
if(條件1){
if(條件2){
//做事情A
可以轉換為:
if(條件1 && 條件2){
//做事情A
如果條件太多,且實在不知道怎麼合并,可以把條件單獨放到一個函數:
public void doSomething(){
if(isRoastDuckNotInBeijing()){
輸出非北京烤鴨。
}else{
輸出北京烤鴨。
}
}
public boolean isRoastDuckNotInBeijing(){
return
注意,如果條件可讀性強:比如sex == “man”就不要把條件放到單獨函數。
2.學會使用return降低層次
public void doSomething(){
if(firstNum!=0){
if(secondNum!=0){
//doSomething
如果改為:
public void doSomething(){
if(firstNum == 0)
return;
if(secondNum == 0)
return;
//doSomething
看起來好多了。
3.學會使用函數分割代碼
我是在上大學的時候學會寫代碼的。有一點解不開的疑惑:為什麼要寫函數?我把代碼從頭寫到尾邏輯不是更加通順嗎?每個教代碼的老師,總是在開始的時候給我們講什麼是變量,什麼是函數,怎麼用?我好想說一句:喂,我還沒決定要用函數呢!
講這部分之前,我先講講這個為什麼要寫函數,講為什麼要寫函數前,我不得不提提二分查找法。
舉個例子:
有十六張撲克牌按照編号從小的順序排列,我讓你找出編号排名第11的,你怎麼找呢?
按照順序一本本找,你要找11次,如果我找的是最後一張,需要找16次(當然這裡我們不讨論倒着找)
使用二分查找:分成一半,前面是1-8,後面是9-16,然後再分成一半9-12和13-16,然後再分成一半9-10和11-12,然後我找到了。我找了4次。發現了嗎?速度比順序查找要快,而且我最多隻需要找4次。當查找的範圍越大,二分查找相對順序查找的優勢就越明顯,平均速度也更快。
好了,懂了二分查找,我們再來弄清楚為什麼要寫函數
舉個例子:假設我要寫100行代碼,僞代碼如下:
public void 寫100行代碼(){
寫1-50行代碼();
寫51-100行代碼();
}
public void 寫1-50行代碼(){
寫1-25行代碼();
寫26-50行代碼();
}
public void 寫51-100行代碼(){
寫51-75行代碼();
寫75-100行代碼();
}
這樣如果我找寫第70行代碼寫什麼,隻要使用二分法的原理:先找寫100行代碼(),然後找寫51-100行代碼();以此類推。
是以我們寫函數的目的就在此:把代碼分割成小的部分,更容易查找。
這樣做有個前提:就是你的函數名必須有意義并且表達準确,否則就更浪費時間,如上文的寫1-50行代碼()和寫51-100行代碼()函數名調換,内容不變,識别起來難度就會變大,你先找寫1-50行代碼(),發現它其實是寫後面50行代碼,這會讓你在邏輯上糾結一段時間,是以再次強調:函數名一定要有意義。
另外還有一點,分割項目不要一次分的太細比如上面的例子,我也可以這樣寫(為了顯示效果,我沒寫省略号,就一行行寫了):
public void 寫100行代碼(){
寫1-5行代碼();
寫6-10行代碼();
寫11-15行代碼();
寫16-20行代碼();
寫21-25行代碼();
寫26-30行代碼();
寫31-35行代碼();
寫35-40行代碼();
寫41-45行代碼();
寫46-50行代碼();
寫51-55行代碼();
寫56-60行代碼();
寫61-65行代碼();
寫66-70行代碼();
寫71-75行代碼();
寫76-80行代碼();
寫81-85行代碼();
寫86-90行代碼();
寫91-95行代碼();
寫96-100行代碼();
}
發現了嗎?這段代碼的可讀性明顯變差了。是以一般情況下這種分割函數(裡面每一句都調用其他函數),分割函數代碼不要超過10句,最好不要超過5句。
和分割函數相對應的,還有一種我稱作邏輯函數,這種函數都是做具體的工作,一般不要超過20行,如下:
public void
特殊情況1:有一堆的參數指派/傳遞:
比如錄入個人資訊:有身高,體重,愛好,優點,缺點,性别,父名,母名…這種可能會有很多資料,可能遠遠超過20行,那麼,就把這部分代碼單獨分割:
public void outputUserInfo(){
System.out.println(getUserInfo());
}
public UserInfo getUserInfo(){
UserInfo userInfo = new UserInfo();
userInfo.setHigh(10);
userInfo.setHeavy(1000);
userInfo.setHobby("泡妞");
userInfo.setAdvantage("很帥");
userInfo.setDisadvantage("我是世界上最帥的!");
userInfo.setFatherName("爸爸");
userInfo.setMotherName("媽媽");
//...此處省略萬字
return
大概是這樣,其實真的無法分割嗎?并沒有這樣說法。上面的内容可分為:身體資訊,偏好資訊和親友資訊,于是上面的一個UserInfo就可以分割為BodyInfo,LoveInfo和RelativesInfo,于是又可以保持在20行之内了
ヾ(◍°∇°◍)ノ゙
特殊情況2:邏輯分支,回調和異常處理
邏輯分支例子:
if(!烤鴨在地球){
輸出非北京烤鴨。
輸出目前地點。
}else if(!烤鴨在陸地){
輸出非北京烤鴨。
輸出目前地點。
}else if(!烤鴨在亞洲){
輸出非北京烤鴨。
輸出目前地點。
}else if(!烤鴨在中國){
輸出非北京烤鴨。
輸出目前地點。
}else if(!烤鴨在北京){
輸出非北京烤鴨。
輸出目前地點。
}else{
輸出北京烤鴨。
}
回調例子:
public void clickButton(){
button.setOnClickListener(
new OnClickListener(){
@Overriade
public void onClick(View v){
輸出目前地點。
輸出北京烤鴨。
}
}
);
}
異常處理例子:
public void methodName(){
try{
輸出北京烤鴨。
輸出目前地點。
}catch(Exception e){
e.printStackTrace();
}
}
上面三種情況:選擇分支,回調和異常處理都會導緻代碼臃腫,可讀性變差,我通常都會把裡面的部分單獨列到新的:
第一種:
合成為:輸出結果(boolean isRoastDuckInBeijing,String address)
因為條件分支往往好起名字,是以我通常會這樣寫
第二種:
clickButtonEvent(){}
因為我不能保證除了輸出北京烤鴨資訊外是否會做些别的什麼,通常這部分的修改頻率較高
第三種:
public void methodNameThrowException() throw Exception{}
通常我會在原名稱後加上ThrowException提醒自己抛出異常,這樣既顯示了差別也降低層次,看起來更舒服了:
public void methodName(){
try{
methodNameThrowException();
}catch(Exception e){
e.printStackTrace();
}
}
public void methodNameThrowException() throw