星期六, 4月 30, 2005

反微軟終結

這個站是我見過最久的網站... (BBS 不算) 終於停了...
哎... 微軟什麼時候才會倒...

星期日, 4月 24, 2005

Pair Programming

去年以來個人一直在 team 裡疾呼 unit-test 的重要。現在整個 team 裡除了幾個不曾跟 heavy-tester 一起合作的人之外,幾乎都了解 test 的好處和重要 (有些人是沒寫test 吃了大虧,有些人先是被逼著寫,後來嘗到了甜頭 )。去年的開發幾乎完全都是個人單打獨鬥,然後單純互相討論,今年這個新專案由六個人組成,好玩的是分工完之後就自動兩兩成對 pair 了。記得去年前 pair 時大家都哇哇叫,今年好像因為個別的不同理由不得不 pair 啊,目前 pair 的分配是:
  • pair A -- 資深(主導性強) + test 新手
  • pair B -- 資深(主導性強) + 中等
  • pair C -- java 新手 + test 新手 (兩人主導性差不多)
上面的資深資淺以 test 的資歷來計,我個人認為 unit-test 的 quality 很適合用來判斷一個 developer 的生產力。

A組沒有 pair 跟本做不下去,完全由資深帶領資淺,以免出差錯,不過資深的步調極快,資淺的步調極慢.........

C組的只能 pair 了,因為其中一人剛學 java.... 另一人功力夠,不過還沒實際作過專案,也沒有 test 的經驗

B組的都有 test 經驗,而且都能夠獨力作業,其實不 pair 好像也沒差,不過看起來 pair 的理由是有人可以互相討論,減輕壓力。

這是現在看到的現象,不知再過三個月會變成如何呢?呵呵,接下來討論一下經驗和構想吧:
  • Personality
我發現讓兩個主導性強的或是兩個主導性弱的 pair 在一起問題會很多。兩個主導性強的會堅持己見,很容易吵在一起,不僅氣氛差,進度也會變慢。兩個弱的則是遇到問題會卡很久,然後慢吞吞的,不曉得東西什麼時候會出來,很危險。
  • Relax Time Control
這個..... 我們目前完全沒有控制作息的時間,目前我的構想是 break 個兩次。讓兩人休息休息,下個禮拜建議大家實施看看好了。
  • Standup Meeting
XP 的早上一開始都會開個幾分鐘的小會議,目前我們也還沒有試過,不過這個大概很難吧... 台灣人好像很不喜歡在制式的會議上發言.... 大家覺得能躲就躲.... 如果這個要推行的話,得將會議弄的輕鬆一點。
  • Pair Rotating
目前我們 team 已經自動 pair 起來了,下一個目標自然是 pair rotating。這個遠大的目標不知何時可以採用?還是先玩個 pair 兩三個月好了,等大家非得 pair 時,再來試試看。

Simplied Struts DispatchAction

Struts 的 Dispatch Action 已經行之有年了,有 Dispatch, Lookup, MappingDispatch... 好幾種可以用。但設定方法都很囉嗦 (網頁,程式,struts-config三者都要 hard-code String 在上面),而且適用的了 A ,就不能用在 B。例如雖然 DispatchAction 可以寫在 URL 上: /foo.do?method=saveOrder ,但是遇到一個 form 需要利用多個 submit button dispatch 時,就不行了。你得轉用 LookupDispatch,可是 LookupDispatch 又需要在 Action 定義一個很醜的 getKeyMethodMap() (裡面全部是 hard-code string)。接下來要分享一個通用 URL/submit button/struts-config.xml 三者設定的 SimpleDispatchAction:

package org.bioinfo.util.struts;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.springframework.web.struts.ActionSupport;
/**
* 簡化的 DispatchAction,並支援Spring的 ActionSupport (如不需要可以換回 Action)
*
* 使用方法:
*
* 繼承此 class,並定義各個 action method,比方說有兩個 method 分別做儲存和刪除:
*
* <code>
* public ActionForward save(ActionMapping mapping, ActionForm form,
* HttpServletRequest request, HttpServletResponse response)
* throws Exception {
* // orderService.save(....)
* }
*
* public ActionForward delete(ActionMapping mapping, ActionForm form,
* HttpServletRequest request, HttpServletResponse response)
* throws Exception {
* // orderService.delete(....)
* }
* </code>
*
* 有三種方式可以設定 dispatch 到哪個 method 上。
*
* (1) submit button 法,直接寫在 submit 的 property 上:
*
* <code>
* <html:form action="/some/work">
* .... some thing ....
*
* <html:submit property="dispatch=save" value="儲存"/>
* <html:submit property="dispatch=delete" value="刪除"/>
* </html:form>
* </code>
*
* 注意 property 裡面的值是 'dispatch=xxxx' 記得要寫等號與 method 名稱,
* 而且大小寫要對,不能空白。當網頁按下 "儲存" 時,則會執行 /some/work.do
* 的 save(...) 的 method。 如果按下 "刪除" 則執行 delete(...),
*
* 建議 -- 這種寫法通常是用在一個 Action 有多個 dispatch method,而每個 method
* 都共用同個 ActionForm
*
* (2) URL 法,接在 URL 後面:
*
* <code>
* <html:form action="/some/work?dispatch=save">
* 或是用 link 也可以
* <html:link action="/some/work?dispatch=save" />
* </code>
*
* 建議 -- 通常用在不需要 ActionForm 的 Action,或者是要將 submit button 法
* 寫成 url 時使用。
*
* (3) struts-config 法,直接寫死在 parameter='dispatch=foo' 上
*
* <code>
* <action
* path="/saveOrder"
* name="SaveOrderForm"
* type="antar.order.web.OrderDispatchAction"
* parameter="dispatch=save" >
* </action>
* <action
* path="/deleteOrder"
* name="DeleteOrderForm"
* type="antar.order.web.OrderDispatchAction"
* parameter="dispatch=delete" >
* </action>
* </code>
*
* 建議 -- 這種寫法通常是為了讓 Action 中每個 dispatch method 使用不同
* 的 ActionForm。一旦寫死在 struts-config 裡,該 mapping 的 path 就不能
* 與 URL 法 或是 submit button 法同時使用。
*
* 最後請注意同一個 request 上,URL 法不能與 submit button 法同時使用:
*
* <code>
* ...........錯誤範例...........
* <html:form action="/some/work?dispatch=save">
* .... some thing ....
* <html:submit property="dispatch=delete" value="刪除"/>
* </html:form>
* </code>
*
* @author ingram
*
*/
public abstract class SimpleDispatchAction extends ActionSupport {

private static final String KEY_VALUE_SEPERATOR = "=";

private final static String KEY = "dispatch";

private static final String VALID_PARAMETER_NAME_PATTERN = KEY + "\\"
+ KEY_VALUE_SEPERATOR + "[a-zA-Z0-9_]+";

private Class clazz = this.getClass();

private Class[] argTypes = new Class[] { ActionMapping.class,
ActionForm.class, HttpServletRequest.class,
HttpServletResponse.class };

private Map dispatchMethods = new HashMap();

public final ActionForward execute(ActionMapping mapping,
ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws Exception {

String methodName = getMethodName(request, mapping);

Method method = null;
try {
Object[] args = { mapping, form, request, response };
method = obtainDispatchMethod(methodName);
return (ActionForward) method.invoke(this, args);
} catch (NoSuchMethodException e) {
throw dealWithMethodProblem(methodName, e);
} catch (IllegalAccessException e) {
throw dealWithMethodProblem(methodName, e);
} catch (InvocationTargetException e) {
throw dealWithMethodProblem(methodName, e);
}

}

private RuntimeException dealWithMethodProblem(String methodName,
Exception e) {
return new RuntimeException(
"can not access dispatching method:["
+ methodName
+ "]. ", e);
}

private Method obtainDispatchMethod(String methodName)
throws NoSuchMethodException {
Method method = (Method) dispatchMethods.get(methodName);
if (method == null) {
method = clazz.getMethod(methodName, argTypes);
dispatchMethods.put(methodName, method);
}
return method;
}

static String getMethodName(HttpServletRequest request,
ActionMapping mapping) {

final List gatherAllMethodNames = new ArrayList();
for (Enumeration e = request.getParameterNames();
e.hasMoreElements();) {
String parameterName = (String) e.nextElement();
addMatchedParameter(gatherAllMethodNames, parameterName);
}

final String[] values = request.getParameterValues(KEY);
if (values != null) {
for (int i = 0; i < values.length; i++) {
gatherAllMethodNames.add(values[i]);
}
}

if (mapping.getParameter() != null) {
addMatchedParameter(gatherAllMethodNames
, mapping.getParameter());
}

if (gatherAllMethodNames.isEmpty()) {
throw new IllegalArgumentException(
"no 'dispatch=methodName' found in parameter");
} else if (gatherAllMethodNames.size() > 1) {
throw new IllegalArgumentException(
"\nMultiple dispatch parameter: " + gatherAllMethodNames
+ " Only one parameter is allowed.");
} else {
return (String) gatherAllMethodNames.iterator().next();
}
}

private static void addMatchedParameter(List gatherAllMethodNames,
String parameterName) {
if (parameterName.matches(VALID_PARAMETER_NAME_PATTERN)) {
gatherAllMethodNames
.add(parameterName.split(KEY_VALUE_SEPERATOR)[1]);
}
}
}
嘿嘿,有了這個統合的 Dispatch,日子就好過多啦!我也另外用同樣的邏輯寫了個 SimpleDispatchActionForm,搭配起來用不錯。

星期六, 4月 16, 2005

靠!EasyMock

靠!真的想自殺,居然誤解了 EasyMock 這麼久!
長久以來,寫 mock 我都是一直在 test case 裡寫個 static inner mock class,供各個 test 使用。當初學這套方法時也調查了很多 tool,但是都沒有完美的 solution,當時的 easymock 只能套在 interface 上,完全不合用。而 Dynamock / jMock 族系的需要繼承特殊的 TestCase,而且他的 "錄製" 過程中呼叫 method 是用 method name 的,像這樣:

mockSubscriber.expects(once()).method("receive").with( eq(message) );

天啊,Subscriber.receive() 的呼叫是用 String,這樣 refactor method name 時不就掛了!!而且真的很難看懂他在幹嘛。所以... 就打消使用 mock tool 的念頭了...
昨個亂逛居然發現 easymock 已經有 extension 可以用在 class 上了!而且還是去年就有了!看看下面的這幾行標準的 easymock 使用:

private MockControl control;

private OrderDAO mockOrderDAO;

protected void setUp() throws Exception {
//MockClassControl 可以替 class 建立 Mock
control = MockClassControl.createStrictControl(OrderDAO.class);
mockOrderDAO = (OrderDAO) control.getMock();
}

public void testSaveOrder() {
//開始預錄
Order order = new Order();
//執行真正的 method 來錄製,而不是用 method name
mockOrderDAO.saveOrder(order);
control.replay();
//錄製完成

OrderServiceImpl service = new OrderServiceImpl();
service.setOrderDAO(mockOrderDAO);
service.saveCustomerOrder(order);
//比對錄製結果
control.verify();
}
就是這麼簡單,唯一的限制是需要非 final 的 public constructor,不過這是小事啦!
居然一直停留在 mock tool 限制很多的印象裡... 該死!

靠~~~~

星期日, 4月 03, 2005

為什麼 Mac 不好?

自從上個月買到了 mac mini 之後,這個月來總算圓了長久以來的 mac 夢,真是過足了癮。但是... 說真的,mac 真的只能當一台放在客廳的電腦,問題真不是普通的多...

What I love Mac:
  • UI 的反應速度不錯,效果也很多,整體的感覺非常的 smooth。這是 windows/linux 遠遠及不上的。
  • Dock 的概念很新,一開始用還不大能適應,因為程式關了視窗卻不是關掉程式本身,只能算是 Hide 而已。不過久而久之,你反而會習慣讓 OS 自己去管理開啟的程式,只要 RAM 夠大 就好 (個人是用 1g) 。完全不用去傷腦筋現在開了多少程式,多少視窗 (windows 的 task bar 就是這樣)
  • Eye candy 很多,很炫很酷。show 給大家看時很爽~~
  • 字形 smooth 之後很漂亮。
  • PDF anywhere,這點就真的很強了。唯有 pdf 才是真正的 portable 文件啊!
  • 好用的 expose ,視窗滑來滑去的好看,找起來也快,設定好之後用滑鼠就能操作,讚。
  • 支援 MS Office, Oracle 9i/10g, Eclipse, Firefox, msn... 等等我每天必用的軟體。
  • Unix based, built-in XWindow, open ldap, and cvs server... etc
  • 支援 palm 的同步,光這個就打死 linux 了。
  • 系統設定很簡單 (網路、硬體.... etc)
  • 有免費的 remote desktop control 可用,有這個真的很方便。
  • Virus/Spyware free !
  • Mac mini: 超小台,可以當 notebook 用了。個人每天帶著這台上下班喔。
What I hate Mac:
  • 中文,中文!!支援中文實在太爛了。
  • 首先 iTune 遇到非unicode 的 id3 tag 中文通通掛掉。是啊,網路上是有轉碼的程式可用,不過只要轉過一次就知道,掉字的掉字,亂碼的亂碼,還是不可行,而且只能轉 mp3,不能轉 mpc/ogg。
  • 第 二,從 windows office 來的檔案打開後中文常常變亂碼。大概是 windows 和 OS X 處理中英字型混用的方式不大相同造成的,也有可能是 office 自己搞爛的。還有就是 office 2004 和一般 windows office 相容性很爛,常常排版都亂掉了,這當然是 MS 的錯 (而且錯的愚蠢) 。但不管是誰造成的,在 Mac 上就是別想好好用 office 了。
  • 第三,中文字 Apple LiGothic 的英文字太小,但偏偏是系統字型,改都不能改,可憐的 台灣 Mac 族長久以來一直不斷忍受... 直到現在,大家還是在忍受...
  • 第四,輸入中文時,有時候第一個字的英文,有時候會是中文,跳來跳去.... 很爛。
  • 第五,palm 是支援了,不過中文上還是有些小問題。而且 palm 已經宣佈不在支援 Mac 了,真糟糕。
  • keyboard binding 與 PC 系統相差太大。PC 都是以 ctrl 鍵當作指令的起點,而 Mac 則是 command 鍵,每個系統 keybinding 不一樣是理所當然... 不過當 Mac 在網頁上時就吃虧了。有些網頁可以用 hotkey 操作地,不過大多是設計給 PC 用的,這時就不相容啦。另外 unix 下的軟體也都是用 ctrl 操作的 (unix 可沒有 command key) 所以如果一旦同時操作 Unix terminal 和其他 OS X 原生軟體,你會發現一會要用 ctrl,一會要用 command,手忙腳亂,效率大減。
  • java,java !! Eclipse 實在太慢了,eclipse 的反應速度跟不上我寫程式的節奏,寫程式變的一頓一頓的,超不爽!
  • Firefox,Firefox !! Firefox 慢死了,受不了的慢~~~
  • JDK 5.0 ! jdk 5.0 已經出半年了,到現在還沒有影子,不要跟我說那個什麼 Tiger preview,當其他 OS 已經有免費的可下載了,誰還要花一堆精神去搞不大能用的半成品 ?而且還非 OSX 10.4 才可用,可笑可笑。
  • 雖 然是 Unix based 的系統,當你要裝 unix 的東東時... 比如 open ldap, cvs server... 時就會發現其實不能照 linux 的方式來裝,有很多地方要改,這也是理所當然,每個 OS 都有自己的設定方式。但是重點是資源少的可憐,甚至是沒有。而且 OSX 還綁了一個 netinfo,搞的都跟大家不一樣,找不到資料時就只好放棄... 。如果花了很多時間學習 OSX 上的 unix,投資報酬率很低的,因為好不容易學了一堆東西,你不見得有地方可以發揮,對工作上幫助並不大 (台灣有幾家公司用 OS X server ??)
  • free 的軟體很多,但是完成度都不是很高,有哪個 Mac 軟體能像 foobar2000 一樣通吃所有格式,超強的 mass tag,外加極佳的音質? 有哪個 Mac 軟體能像 filezilla 一樣提供全功能的 ftp client,可以 queue file transfer?有哪個 Mac 軟體能像 xnview 一樣超快速的瀏覽圖片外加 batch 修改圖的功能?有哪個 Mac 軟體可以像 7-zip 一樣通吃所有格式,提供方便的 UI ? (7-zip windows 的 UI 並不怎麼樣,但是 Mac 的更不像話!)
  • Eye candy 是不錯啦... 不過看久了 (我用了一個月) 就覺得沒什麼了,還是效率比較重要。
  • Mac 的東西貴,太貴了。我現在買的是 apple 大放送的 mac mini,如果萬一未來被 Mac 綁死了,就只好繼續買 Mac,一台像樣的 powerbook 多少錢? 9萬元!天啊~~~
大致上來說,只要有軟體跨平台跨到 Mac ,通常在 Mac 的效率都是最差的,偏偏這些跨平台的軟體都是公認最好用的,例如 firefox, eclipse (軟體可以跨平台跨到 Mac 表示極受大家愛用)。我想了一想,效率差原因可能是:
  • Mac 上程式不好寫 or 不好最佳化
  • Mac 的 (好) developer 少
  • Mac 的硬體太慢
  • 那些程式一開始就不是在 Mac 上寫的,是 port 過去的
第 一點比較難判斷,而 firefox 和 eclipse 慢的原因應該是第四點吧... 不過第二、第三點也不無可能。Mac 用的人少,自然吸引不了什麼開發者,很多公司後來也只出 windows 版的軟體了。Mac 的硬體慢嗎?也許某些領域 Mac 不錯,不過就 notebook (mac mini 算是歸在 notebook 內) 來講 Mac 已經落後 PC 太多。PC 在 intel/AMD 兩家的競爭嘶殺之下,進步快多了。而所謂的 G5 powerbook 也還生不出來,就算生出來了一定是爆貴 (台幣 9~10萬 吧,誰買的起?) 而且也不見得比 PC based 的 notebook 快。

哎.... 看來還是得回 Windows... 沒錢玩不起高貴的 Mac

Eclipse tips on source code

Try to attach source codes for *.jar, then the auto-generated source (such as method) will comes with meaningful name, for example, A generated execute(...) method of Struts Action:

without attach source:
    public ActionForward execute(
ActionMapping arg0,
ActionForm arg1,
HttpServletRequest arg2,
HttpServletResponse arg3
throws Exception {
}
lots of ugly arg0, arg1.... !!!

after attach Struts source codes to struts.jar:
    public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
}
whew! much better !

Another tip is when you attach source codes, you can view java docs without assigning javadoc location. Eclipse will parse javadocs that inside attached source codes for you.