2015年11月5日 星期四

Android StackTrace

2015/11/05 15:10~15:31

在C語言中,Log時可以使用 __func__  __LINE__ 取得目前程式所執行的函式和行數,在Android中的StackTraceElement物件也提供了類似的功能,它記錄了Call stack的許多資訊,包括執行檔案名稱、程式碼行號、方法(或函數)名稱、類別名稱。

程式範例:

package com.example.getstacktrace;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         trace("test");
    }

    private void trace(String msg) {
         StackTraceElement[] stack = Thread.currentThread().getStackTrace();        
         for(int i = 0; i < stack.length; i++) {
             StackTraceElement s = stack[i];
             Log.d("Brian", "i=" + i);
             Log.d("Brian", "ClassName="+s.getClassName());
             Log.d("Brian", "FileName="+s.getFileName());
             Log.d("Brian", "MethodName="+s.getMethodName());
             Log.d("Brian""LineName="+s.getLineNumber());      
             i ++;
         }
         Log.d("Brian ", msg);
    }

}

輸出結果:
11-05 15:25:49.748: D/Brian(6662): i=0
11-05 15:25:49.748: D/Brian(6662): ClassName=dalvik.system.VMStack
11-05 15:25:49.748: D/Brian(6662): FileName=VMStack.java
11-05 15:25:49.748: D/Brian(6662): MethodName=getThreadStackTrace
11-05 15:25:49.748: D/Brian(6662): LineNumber=-2
11-05 15:25:49.748: D/Brian(6662): i=1
11-05 15:25:49.748: D/Brian(6662): ClassName=java.lang.Thread
11-05 15:25:49.748: D/Brian(6662): FileName=Thread.java
11-05 15:25:49.748: D/Brian(6662): MethodName=getStackTrace
11-05 15:25:49.748: D/Brian(6662): LineNumber=599
11-05 15:25:49.748: D/Brian(6662): i=2
11-05 15:25:49.748: D/Brian(6662): ClassName=com.example.getstacktrace.MainActivity
11-05 15:25:49.748: D/Brian(6662): FileName=MainActivity.java
11-05 15:25:49.748: D/Brian(6662): MethodName=trace
11-05 15:25:49.748: D/Brian(6662): LineNumber=16
11-05 15:25:49.748: D/Brian(6662): i=3
11-05 15:25:49.748: D/Brian(6662): ClassName=com.example.getstacktrace.MainActivity
11-05 15:25:49.748: D/Brian(6662): FileName=MainActivity.java
11-05 15:25:49.748: D/Brian(6662): MethodName=onCreate
11-05 15:25:49.748: D/Brian(6662): LineNumber=12
11-05 15:25:49.748: D/Brian(6662): i=4
11-05 15:25:49.748: D/Brian(6662): ClassName=android.app.Activity
11-05 15:25:49.748: D/Brian(6662): FileName=Activity.java
11-05 15:25:49.748: D/Brian(6662): MethodName=performCreate
11-05 15:25:49.748: D/Brian(6662): LineNumber=5165
11-05 15:25:49.748: D/Brian(6662): i=5
11-05 15:25:49.748: D/Brian(6662): ClassName=android.app.Instrumentation
11-05 15:25:49.748: D/Brian(6662): FileName=Instrumentation.java
11-05 15:25:49.748: D/Brian(6662): MethodName=callActivityOnCreate
11-05 15:25:49.748: D/Brian(6662): LineNumber=1103
11-05 15:25:49.758: D/Brian(6662): i=6
11-05 15:25:49.758: D/Brian(6662): ClassName=android.app.ActivityThread
11-05 15:25:49.758: D/Brian(6662): FileName=ActivityThread.java
11-05 15:25:49.758: D/Brian(6662): MethodName=performLaunchActivity
11-05 15:25:49.758: D/Brian(6662): LineNumber=2416
11-05 15:25:49.758: D/Brian(6662): i=7
11-05 15:25:49.758: D/Brian(6662): ClassName=android.app.ActivityThread
11-05 15:25:49.758: D/Brian(6662): FileName=ActivityThread.java
11-05 15:25:49.758: D/Brian(6662): MethodName=handleLaunchActivity
11-05 15:25:49.758: D/Brian(6662): LineNumber=2521
11-05 15:25:49.758: D/Brian(6662): i=8
11-05 15:25:49.758: D/Brian(6662): ClassName=android.app.ActivityThread
11-05 15:25:49.758: D/Brian(6662): FileName=ActivityThread.java
11-05 15:25:49.758: D/Brian(6662): MethodName=access$600
11-05 15:25:49.758: D/Brian(6662): LineNumber=162
11-05 15:25:49.758: D/Brian(6662): i=9
11-05 15:25:49.758: D/Brian(6662): ClassName=android.app.ActivityThread$H
11-05 15:25:49.758: D/Brian(6662): FileName=ActivityThread.java
11-05 15:25:49.758: D/Brian(6662): MethodName=handleMessage
11-05 15:25:49.758: D/Brian(6662): LineNumber=1370
11-05 15:25:49.758: D/Brian(6662): i=10
11-05 15:25:49.758: D/Brian(6662): ClassName=android.os.Handler
11-05 15:25:49.758: D/Brian(6662): FileName=Handler.java
11-05 15:25:49.758: D/Brian(6662): MethodName=dispatchMessage
11-05 15:25:49.758: D/Brian(6662): LineNumber=99
11-05 15:25:49.758: D/Brian(6662): i=11
11-05 15:25:49.758: D/Brian(6662): ClassName=android.os.Looper
11-05 15:25:49.758: D/Brian(6662): FileName=Looper.java
11-05 15:25:49.758: D/Brian(6662): MethodName=loop
11-05 15:25:49.758: D/Brian(6662): LineNumber=158
11-05 15:25:49.758: D/Brian(6662): i=12
11-05 15:25:49.758: D/Brian(6662): ClassName=android.app.ActivityThread
11-05 15:25:49.758: D/Brian(6662): FileName=ActivityThread.java
11-05 15:25:49.758: D/Brian(6662): MethodName=main
11-05 15:25:49.758: D/Brian(6662): LineNumber=5777
11-05 15:25:49.758: D/Brian(6662): i=13
11-05 15:25:49.758: D/Brian(6662): ClassName=java.lang.reflect.Method
11-05 15:25:49.758: D/Brian(6662): FileName=Method.java
11-05 15:25:49.758: D/Brian(6662): MethodName=invokeNative
11-05 15:25:49.758: D/Brian(6662): LineNumber=-2
11-05 15:25:49.758: D/Brian(6662): i=14
11-05 15:25:49.758: D/Brian(6662): ClassName=java.lang.reflect.Method
11-05 15:25:49.758: D/Brian(6662): FileName=Method.java
11-05 15:25:49.758: D/Brian(6662): MethodName=invoke
11-05 15:25:49.758: D/Brian(6662): LineNumber=511
11-05 15:25:49.758: D/Brian(6662): i=15
11-05 15:25:49.758: D/Brian(6662): ClassName=com.android.internal.os.ZygoteInit$MethodAndArgsCaller
11-05 15:25:49.758: D/Brian(6662): FileName=ZygoteInit.java
11-05 15:25:49.758: D/Brian(6662): MethodName=run
11-05 15:25:49.758: D/Brian(6662): LineNumber=1083
11-05 15:25:49.758: D/Brian(6662): i=16
11-05 15:25:49.758: D/Brian(6662): ClassName=com.android.internal.os.ZygoteInit
11-05 15:25:49.758: D/Brian(6662): FileName=ZygoteInit.java
11-05 15:25:49.758: D/Brian(6662): MethodName=main
11-05 15:25:49.758: D/Brian(6662): LineNumber=850
11-05 15:25:49.758: D/Brian(6662): i=17
11-05 15:25:49.758: D/Brian(6662): ClassName=dalvik.system.NativeStart
11-05 15:25:49.758: D/Brian(6662): FileName=NativeStart.java
11-05 15:25:49.758: D/Brian(6662): MethodName=main
11-05 15:25:49.758: D/Brian(6662): LineNumber=-2
11-05 15:25:49.758: D/Brian(6662): test



挑自己需要的StackTraceElement包成Method就很好Debug了!

2015年10月26日 星期一

印出環境變數

2015/10/26 11:38~11:54


在Windows中,很多時候需要用命令提示字元觀看環境變數,在Powershell中,這時可以使用下面的命令

$env:Path

輸出結果:

C:\Windows\Microsoft.NET\Framework\v4.0.30319;C:\ProgramData\Oracle\Java\javapath;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files\Microsoft SQL Server\110\Tools\Binn\;C:\ProgramFiles\TortoiseSVN\bin;C:\HashiCorp\Vagrant\bin;C:\Program Files\SourceGear\Common\DiffMerge\;C:\Program Files (x86)\Skype\Phone\


在Windows的環境變數中,每一個路徑使用分號隔開,直接印出來很難閱讀有哪先路徑。


這時可以把分隔符號分號取代為跳行,結果如下:

($env:Path).Replace(';', "`n")

輸出結果:

C:\Windows\Microsoft.NET\Framework\v4.0.30319
C:\ProgramData\Oracle\Java\javapath
C:\Windows\system32
C:\Windows
C:\Windows\System32\Wbem
C:\Windows\System32\WindowsPowerShell\v1.0\
C:\Program Files\Microsoft SQL Server\110\Tools\Binn\
C:\ProgramFiles\TortoiseSVN\binC:\HashiCorp\Vagrant\bin
C:\Program Files\SourceGear\Common\DiffMerge\
C:\Program Files (x86)\Skype\Phone\

Powershell用起來比命令提示字元順好多!

2015年7月29日 星期三

持續改善實戰演練

2015/7/28 12:25-13:27,
2015/7/29 00:31-01:14


今天打的這篇將會出現一位未出現過的人物,名為Joanne,是一位嚴重受科技控制的女孩,一起出去時不時就會拿起手機喵一下是否有新訊息,吃飯吃到一半也不忘要拿起我的Lumia 925,技術純熟地開啟我的FB塗鴉牆或者instagram,看看我有沒有偷偷跟哪個小妹妹聯絡感情。看到這邊應該可以猜出她和我的關係了吧?沒錯!她就是我家的那隻啦XD

---

事情發生在7/25 (六)

這天很巧,和可愛的Joanne大人去吃一家員林赫赫有名的希拉餐廳,燈光美氣氛佳,可惜我那不給力的FB塗鴉牆出現多年不見的要好女性友人PO了疑似看見我的文章。

離開希拉之後,路過一間名字聽起來不錯的迥畫廊咖啡,點了幾杯飲料開始接受Joanne的質問。

---

Joanne:「她PO這個是因為看到我們嗎?」

我:「不會吧!我已經仔細環顧四周了,沒有看到我認識的人啊!」

Joanne:「她幹嘛PO的好像在躲我們一樣,這樣真的很不夠意思!」

我:「她的動態對我們來說也沒有很重要啊,這麼在乎幹嘛。」

---

講完之後我自己我有點生氣,為什麼要在意一個不是很重要的人這麼多!於是我專心看著咖啡店裡的,Joanne也很有默契地滑著手機,我倆不發一語,一直到我看到第26頁的時候,終於突破僵局手拉著手離開咖啡廳。然後從此過著幸福快樂的一天~~~了嗎?

當然沒有,這只是個小小的火種,悄悄的埋在Joanne的心中。

回到家約晚上七點多,老爸一如往常地看到我們見聊個兩句

老爸:「Joanne啊,你很不夠意思欸!我以為你今天回來會煮飯給我吃,害我等你們等這麼久」(老爸是講中文,以上對話純屬修改)

我聞到Joanne生氣的壞味道,趕緊出面緩和一下場面

我:「老爸,Joanne最近在台北工作很累,她是回來渡假休息的啦!不要為難一個正在渡假的人啊!」

老爸:「那我去煮個拿手的泡麵來孝敬你們兩個正在渡假的人要不要?」

講到這邊Joanne就委屈的跑去廁所。

老爸又接著說:「Joanne怎麼時常跑廁所,你要關心一下她。」

說完老爸也出去溜搭了,然後我開始看著週六晚檔,連播四集,共兩小時的動畫。

身為一個電視迷,說什麼也要排除順利觀賞節目的阻礙,我專心地看著電視裡生動的動畫,Joanne從廁所出來後,就零音量地坐在一旁玩筆電、手機。就連在動畫廣告時,我故意向空氣發出的問句,她也不為所動。但為了順利觀賞動畫演出。顧不得Joanne的任性,只好努力地催眠自己沒有任何事情發生。

就在動畫演完的那一刻,我的感官神經從電視飛回到我的身體,驚覺到身旁有股能量準備爆發,雖想當作沒事地把時間拖過去。但想到如果不讓Joanne爆發,不就枉費了從Teddy學來的人生道理軟體工程嗎?

敏捷開發的最大優點就是能透過一連串的活動讓問題浮現出來,發現問題之後,才有機會對症下藥。於是心裡開始有個聲音默默出現:「如果連一個人都沒辦法搞定,那怎麼可能有辦法搞定整個團隊?」

我跨出了很勇敢的第一步:「你生氣是不是因為爸爸說你怎麼沒有煮菜給他吃?」

此話一出,馬上引來Joanne的情緒爆炸,配著滔滔不絕的眼淚和哽咽又有磁性地聲音:「我今天跟你說很多事情,你都在第一時間否定我,都說這是小事不用在意,向你表達我的感覺,卻沒有得到情緒上的安慰,在你們家我是一個外人,如連你都不站我的立場替想,那我不是很孤單委屈嗎?你爸爸這樣說,我怎麼知道是開玩笑,我們的家庭教育就是不一樣啊!#$%@$#%^%$#^%#$%^」 講的太長我也忘記到底在講什麼XD

不過算是發現了Joanne的情緒崩潰點算是有點進展,吵了將近三十分鐘後,套入Retrospective,將今天所有事件的處理方式列出優點和缺點。

Could be better:

1. 我們兩個的個性一個大而化之;一個心思縝密,要站在Joanne立場多位她說話。
2. 你(Brian)遇到問題每次都沉默
3. 你(Brian)應該是最了解我(Joanne)的人,會知道我(Joanne)的心情

Good:

1. 查覺到異狀有即時出手幫Joanne說話,雖然說得很爛(渡假是三小?家長聽了會怎麼想?)
2. 舉行Retrospective讓雙方有理性溝通的機會


講完之後就和好了,再努力再加油,朝著更幸福快樂的日子努力!

以上內容由事實改編。這次網誌Delay了三天。

2015年7月18日 星期六

為自己寫一個Pattern

2015/7/18 14:16-15:01

固定每個星期六來想一篇網誌的今天,剛剛好是自己的生日,在生日的這天,來反省一下自己。

目前覺得自己對一件事情的看法太少,常常讓我想起<少年Pi>裡面,Pi的父親和Pi說過的一句台詞:「如果你什麼都信,就代表著你什麼都不信。」

對於生活上瑣碎的事情,每當和死黨聊天的時候,多多少少會聊到一些對於社會議題的看法,我發現自己通常對於發生的事情都抱持著淡定的態度,其實想發表一些什麼,但卻發現自己講不出個什麼。呈現一個「腦死」狀態。

來練習一下把自己的狀況寫成Pattern。


Context:
€  一個平常對於任何事情或突發狀況都很淡定的人

Problem:
€  如何培養對於週邊事物發表個人看法的能力?

Force:
€  平常沒有動腦的習慣
€  發表自己的看法後怕別人見笑
€  身旁的人難以溝通,自己懶得費工
€  腦袋空空好像怎麼講都不對

Solution:
€  揪一個朋友看每天花同樣的時間看一樣的書,每天看完的當下就討論所看到的內容


前進速度緩慢

2015年7月12日 星期日

Martin Fowler整理的重構名錄

2015/7/2 20:34-21:20

前幾天終於把<重構>這本書讀完一次了,這本書有一半算是工具書,介紹非常多的重構手法,每個重構手法都以一個例子介紹,每個重構手法通常都可以找到它的情侶XD,原本正方形的東西不好看,所以凹成的長方形,凹長方形之後看起來有覺得怪怪的,於是又凹回了正方形。

在寫程式的時候,重構通常是為了讓程式碼更乾淨,但有時候還是會被"設計"綁住,對於還沒碰到的問題,有了過多的設計,也許在這個當下,你認為將來系統會遇到某些的擴充,所以你為了預防這個很久很久才會到來新需求或是非常偶爾才會出現的Special Case,開始重構程式,把原本功能簡單的程式碼切成很多Class去實作,以便以後可以應付這種Special Case。過了一段期間,才發現之前太杞人憂天了,很多預想的狀況根本都沒有發生,於是開始反重構,把很多冗員類別刪除。

這本書厲害的地方就在於每一個重構手法都以很小的步驟慢慢前進,在實際的工作中,可以先培養聞壞味道的能力,然後遇到某種壞味道的時候,在把這本書拿出來翻到最後一頁,參考下面的兩張表格,找出能解決這個壞味道的重構手法。


擷自<重構>最後一頁


擷自<重構>最後一頁

---



最近感冒,一直覺得頭好暈阿,差點兒沒發網誌。

讀完中文版的第一次,再去借了一本英文版,即使看過一次中文版的,英文版的讀起來還是頗硬阿!

先把一本原文書重頭到尾讀三次→Teddy Chen教我的那些事

2015年7月4日 星期六

程式碼的壞味道(3)

2015/7/3 21:58-22:44


上兩篇節錄了16個在重構中所提到的壞味道,
<程式碼的壞味道(1)>
<程式碼的壞味道(2)>

剩下6個壞味道,今天終於要把它們全部抄完節錄了


---

17. Inappropriate Intimacy (狎暱關係)

有時你會看到兩個classes過於親密,花費太多時間去探究彼此的private成分。如果這發生在兩個「人」之間,我們不必作衛道之士;但對於classes,我們希望他們嚴守清規。


18. Alternative Classes with Different Interfaces (異曲同工的類別)

兩個函式做同一件事,卻有著不同的署名式(signature)


19.Incomplete Library Class (不完美的程式庫類別)

復用(reuse)常被視為物件的終極目的。我們認為這實在是過度估計了(我們只是使用而已)。但無可否認,許多編程技術都建立在library classes的基礎之上,沒人敢說是不是我們都把排序演算法忘得一乾二淨了。Library classes構築者沒有未卜先知的能力,我們不能因此責怪他們。畢竟我們自己也幾乎總是在系統快要構築完成的時候才能弄清楚它的設計所以library構築者的任務真的很艱鉅。麻煩的是library的形式(form)往往不夠好,往往不可能讓我們修改其中的classes使它完成我們希望完成的工作。


20. Data Class (純稚的資料類別)

所謂Data Class是指:它們擁有一些欄位(fields),以及用於存取(讀寫)這些欄位的函式,除此之外一無長處。這樣的classes只是一種「不會說話的資料容器」,它們幾乎一定被其他classes過分細瑣地操控著。


21.Refused Bequest (被拒絕的遺贈)

subclasses應該繼承superclass的函式和資料。但如果它們不想或不需要繼承,又該怎麼辦呢?它們得到所有的禮物,卻只從中挑選幾樣來玩!


22.Comments (過多的註釋)

從嗅覺上來說,Comments不是一種壞味道:事實上它們還是一種香味呢。我們之所以要在這裡提到Comments,因為人們常把它們當作除臭劑來使用。常常會有這樣的情況:你看到一段程式碼有著常常的註釋,然後發現,這些註釋之所以存在是因為程式碼很糟糕。這種情況的發生次數之多,實在令人吃驚。
Comments可以帶我們找到本章先前提到的各種壞味道。找到壞味道之後,我們首先應該以各種重構手法把壞味道去除。完成之後我們常常會發現:註釋已經變得多餘了,因為程式碼已經清楚說明了一切。

『當你感覺需要撰寫註釋,請先嘗試重構,試著讓所有註釋都變得多餘。』


如果你不知道該做什麼,這才是註釋的良好運用時機。除了用來記述將來的打算之外,註釋還可以用來你並無十足把握的區域。你可以在註釋裡寫下自己「為什麼做某某事」。這類資訊可以幫助將來的修改者,尤其是那些健忘的傢伙。


---

書中的這個章節,我覺得是全書最精華的地方,之前翻了一點<深入淺出程式設計>,雖然有認識到一些Pattern,但是一直無法找到應用某個Pattern的時機。

就在今天利用Extract Super Class重構的時候,突然發現重構完的地方可以套用Command Pattern,突然有一種串起來的感覺,頗有成就感XD。



每個軟體工程師都應該來讀一下重構這本書。

2015年6月27日 星期六

程式碼的壞味道(2)

2015/06/27 22:29-23:37

今天延續上一篇的壞味道繼續節錄XD


9. Primitive Obsession (基本型別偏執)


大多數的編程環境都有兩種資料:結構型別(record types)允許你將資料組織成有意義的形式;基本型態(primitive types)則是構成結構型別的積木塊。結構總是會帶來一定的額外開銷。一般程式語言中物件技術的新手通常不願意在小任務上運用小物件。


10. Switch Statements (switch驚悚現身)

物件導向程式的一個最明顯特徵就是:少用switch(case)述句。從本質上說,switch述句的問題在於重複(duplication)。你常會發現同樣的switch述句散佈於不同地點。如果要為它添加一個新的case子句,你必須找到所有switch述句並修改它們。


11. Parallel Inheritance Hierarchies (平行繼承體系)

在這種情況下,每當你為一個class新增一個subclass,必須也為另一個class相應增加一個subclass。如果你發現某個繼承體系的class名稱字首和另一個繼承體系的class名稱字首完全相同,便是聞到了這種壞味道。


12. Lazy Class (冗員類別)

你所創建的每一個class,都得有人去理解它、維護它,這些工作都要花錢的。如果一個class的所得不值其身價,它就應該消失。


13. Speculative Generality (夸夸其談未來性)

當有人說『噢,我想我們總有一天需要做這種事』並因而企圖以各式各樣的掛勾(hooks)和特殊情況來處理一些非必要的事情,這種壞味道就出現了。那麼做的結果往往造成系統更難理解和維護。如果所有裝置都會被用到,那就值得這麼做;如果用不到,就不值得。用不上的裝置只會擋你的路,所以,把它移開吧。


14. Temporary Field (令人迷惑的暫存欄位)

有時你會看到這樣的物件:其內某個instance變數僅為某種特定情勢而定。這樣的程式碼讓人不易理解,因為你通常認為物件在所有時候都需要它的所有變數。在變數未被使用的情況下猜測當初其設置的目的,會讓你發瘋。


15. Message Chains (過度耦合的訊息鏈)

如果你看到用戶向一個物件索求(request)另一個物件,然後再向後者索求另一個物件,然後再索求另一個物件這就是Message Chain。程式碼中你看到的可能是一長串的getThis()或一長串暫存變數。採取這種方式,意味客戶將與搜尋過程中的航行結構(structure of navigation)緊密耦合。一旦物件間的關係發生變化,客戶端就不得不作出相應修改。


16. Middle Man (中間轉手人)


物件的基本特徵之一就是封裝(encapsulation)-對外部世界隱藏其內部細節。封裝往往伴隨著委託(delegation)。比如說你問主管是否有時間參加一個會議,他就把這個訊息委託給他的記事簿,然後才回答你。很好,你沒必要知道這位主管到底使用傳統記事簿抑或秘書來記錄自己的約會。但是人們可能過度運用delegation。你也許會看到某個class介面有一半的函式都委託給其他class,這樣就是過度運用。這時應該直接和實責物件打交道。


原來switch這麼驚悚,我卻不知不覺跟它相處了這麼多年...

2015年6月20日 星期六

程式碼的壞味道(1)

2015/06/20 21:07-22:38

今天來節錄<<重構-改善既有程式的設計>>書中提到的程式碼壞味道。


  1. Duplicated Code (重複的程式碼)
  2. Long Method (過長函式)
  3. Large Class (過大類別)
  4. Long Parameters List (過長參數列)
  5. Divergent Change (發散式變化)
  6. Shotgun Surgery (霰彈式修改)
  7. Feature Envy (依戀情節)
  8. Data Clumps (資料泥團)
  9. Primitive Obsession (基本型別偏執)
  10. Switch Statements (switch驚悚現身)
  11. Parallel Inheritance Hierarchies (平行繼承體系)
  12. Lazy Class (冗員類別)
  13. Speculative Generality (夸夸其談未來性)
  14. Temporary Field (令人迷惑的暫存欄位)
  15. Message Chains (過度耦合的訊息鏈)
  16. Middle Man (中間轉手人)
  17. Inappropriate Intimacy (狎暱關係)
  18. Alternative Classes with Different Interfaces (異曲同工的類別)
  19. Incomplete Library Class (不完美的程式庫類別)
  20. Data Class (純稚的資料類別)
  21. Refused Bequest (被拒絕的遺贈)
  22. Comments (過多的註釋)

首先簡單節錄前八個壞味道:

1.     Duplicated Code (重複的程式碼)
最單純的情況就是同一個類別(Class)內的兩個函式函有相同的實作內容

2.     Long Method (過長函式)
不熟悉物件導向的人,常常覺得物件程式中只有無窮無盡的委託(delegation),根本沒有進行任何計算。和此類程式共同生活數年之後,你才會發現這些小小函式有多大的價值。解釋能力、共享能力、選擇能力都是由小型函式支援的。

3.     Large Class (過大類別)
想利用單一類別(Class)做太多事情,其內往往就會出現太多instance變數。一旦如此,Duplicated Code也就解踵而至了。

4.     Long Parameters List (過長參數列)
以前學C語言,老師教我們:把函式所需的所有東西都以參數傳遞進去。這可以理解,因為除此之外就只能選擇全域資料,而全域資料是邪惡的東西。有了物件,你只需傳給它足夠的東西,讓函式從中獲得自己所需要的所有東西就行了。

5.     Divergent Change (發散式變化)
一旦需要修改,我們希望能夠跳到系統的某一點,只在該處作修改。如果不能做到這點,你就嗅出兩種緊密相關的刺鼻味道中的一種了。
如果某個class經常因為不同的原因在不同的方向上發生變化,Divergent Change這個壞味道就出現了。例如:當你看著一個class說:『呃,如果加入一個資料庫,我必須修改這三個函式;如果新出現一種金融工具,我必須修改這四個函式』。

6.     Shotgun Surgery (霰彈式修改)
類似Divergent Chane,但恰恰相反。Divergent Change是指「一個class受多種變化的影響」,Shotgun Surgery則是指「一種變化引發多個classes相應修改」。

7.     Feature Envy (依戀情節)
我們看到某個函式為了計算某個值,從另一個物件那兒呼叫幾呼半打的取值函式(getting method)

8.     Data Clumps (資料泥團)

兩個classes內的相同欄位(field)、許多函式署名式(signature)中的相同參數。


---


本來要去圖書館拿有關UML的書,看到這本書在書架上發光就索性帶回家。
借回來五天,利用交通和空閒時間看,已經讀了140頁。

如果已經碰過程式有幾年經驗,這本書應該能講到你的心坎裡。但是如果你是學程式初學者應該會看得"霧颯颯"。


先聞出壞味道會比較清楚重構的時機