2010年4月29日 星期四

X18 隨時檢查自己做的動作

某天開會,幾位同事各在白板上寫了一些字。有位同事忽然用羨慕的口吻對我說:“你的字寫的好整齊,而且每個字的大小都差不多;不像我寫的字,不僅大小不一而且還會越寫越往下。”我微笑著說,寫成這樣並沒有什麼,只要在幾個下筆的時機做一些檢查就可以辦到。

●寫第一個字時,決定所有字型的大小
●寫第二個(含)以後的字下第一筆時,要決定與前字的間隔與水平位置
●寫字的每個筆劃時,都要寫出與字型大小相符的筆劃
●寫第二行(含)以後的第一個字時,要決定與上一行的垂直間隔
●總之,寫每一筆前的瞬間都先做一次檢查並調整

同事心存疑惑地在白板上依照這些原則試寫十多個字後停下來看了看,滿意地說原來自己的字也可以寫得這麼整齊。我回答道:”是啊,每個人下筆前隨時檢核自己要做的動作,就可以擁有整齊的字。 ”

前幾天有人要我依影印稿幫忙在電腦上畫出一張建築物的平面簡圖,當時所有的電腦裡都沒有任何的繪圖軟體,我回答道:“就讓我這個小畫家之王(因為我不會別的)用小畫家來畫吧!”。下方的平面圖是半個小時之後的產出:


用小畫家畫略為嚴謹的圖時,我遵循著兩個原則:
●先計算比例尺,計算出一公分的長度等於圖上的多少點
●該直的絕對是直的,該圓的也絕對是圓的;橢圓則先量出長與寬的數值

畫每一個線條時,先決定起點、再決定方向、最後決定長度。隨時對每一條線做以上的檢查,自然能保證該線條的正確性;每一條線都在正確的位置上作正確的呈現時,整張圖表現出來的自然就會是正確的結果。

註:十多年前工作上急需印出一張軸承的規格(必須用autocad畫),當時只有一張手繪的底稿,而且會用autocad的人全都不在,我只好使用小畫家彷照autocad的表示法慢慢地畫出該軸承的三視圖。最後,對方接受了那張圖。

2010年4月20日 星期二

Y20 錦囊之計 vs 設計程度

話說古代有位軍師總會在出征前拿個錦囊給統率的將軍,要他在不知如何是好的狀況下照內容做,就能逢凶化吉或是出奇制勝。

在此處暫且將時空切割為二:甲時空的軍師通常在出征前一刻根據最新軍情花一點點時間寫錦囊,將領大多時候依錦囊行事倒也正確,但是總有一兩成的機率無法應付現況,需要派人再回去稟報軍情後重新跟軍師拿一個錦囊;乙時空的軍師都會在出征前一天思考整個晚上後,在錦囊中寫下各種可能發生的情況並逐一列舉不同的應對方法,據說不符機宜的機率降低到半成以下。

在這裡說的將軍是在客戶戰場中衝鋒的專案人員,軍師則是指公司裡製作共用元件(錦囊)的支援人員。如果你是軍師,會想用多少精力寫出什麼樣的錦囊?如果你是將軍,又會希望拿到手的是什麼樣的錦囊呢?

身邊絕大多數的人都屬於將軍型,能夠快速使用資源做出客戶想要的結果,遇到不符需求的變化時也能快速地應變;只有很少數的人在面對需求時會如同軍師般思考各種可能情況再作整理設計。將軍型看軍師型會感覺在設計同樣的功能時花較多的時間且產出額外用不到的方法,軍師型看將軍型的產出卻又感覺思慮不夠周延又難以捕捉改變後的影響。更頭痛的是,有些將軍在拿到後方送來的木牛流馬時會嫌錦囊寫的文件看不懂,而且擔心萬一故障時前線不能修理將影響軍情,乾脆重新打造一套很類似卻只有自己懂的東西才會有安全感。

將軍型與軍師型的角色,在雙方的定位清楚並且相互信任的前提下,是可以發揮很大功效的。公司製作共用元件時依照標準開發流程控制品質,提供面對不同狀況下的對應參數;專案人員信任共用元件與正確性與方便性,快速地依客戶需要組裝出該有的功能。這樣不就是很有效率的開發團隊嗎?

註:某個功能在遇到例外狀況,而且從模組內修正那個例外非常麻煩時,將軍型通常會在模組外部添加某些東西,並在模組內部最前端加上判斷添加物是否存在的條件另外處理。軍師型則會堅持分析模組內的處理流程,直到找出可以明確判斷出例外狀況且不影響其他狀況的正解為止;如果找不到這樣的解答,會認為那個模組是個失敗之作。

2010年4月16日 星期五

Y19 First Practice(4)──設計階段

最近很努力地直接開發工具程式,眼看著功能慢慢堆砌出來,也逐步給專案人員協助作測試與試用。

兩週後的某一天,主管問這次開發的工具程式能否在未來重用其中的部分?我愣了一下,由於撰寫中逐漸感覺系統有種“被綁死”的感覺,便回答說這個工具已經因為趕時間而寫壞,不可能切割出可重用的模組。以下就是我給主管的原因:

●沒有全面定義與使用介面
直接寫程式時,對物件常缺乏時間定義應有的行為。在需要對物件新增方法時,會感覺介面得多花時間去設定與調整,再加上認定系統就只會使用這種實作的物件,就順手地將方式寫在Class上。我們都很明白沒有使用Interface會讓物件間的耦合度很高……。



取得物件的途徑不一致
上圖是工具程式的顯示畫面,最外面的MainFrame簡單地分為左右兩個Panel再各自盛裝UI元件。最外層的MainFrame設計為singleton,在設計時希望一邊Panel的UI物件可以存取到另一個Panel上的任一個UI物件來操作。例如左邊的Clear Data按鈕要能操作到右邊的Table以便做到清除的動作。但是程式寫到一半會發現並不是每個Button“都一致定義”擁有Panel物件,再加上趕時間而“懶得”調整,因此取法變為兩種(甚至更多):

1) getLeftPanel().getMainFrame().getRightPanel().getTable();
2) MainFrame.getInstance().getRightPanel().getTable();


●物件操作的方式未統一
原本定義了ModelService來負責變更Model的工作,但有時“難免忘記”應該經由ModelService來做事而直接呼叫Model的修改方法,形成跳層的使用。如此一來,層次間的關聯變得複雜,而且沒法保證要做的事都會經過ModelService。

●忽略行為的封裝
需求裡提到使用者按下Clear Data時要清除右邊的Table,所以開發時直接在它的listener內直接寫下:

getLeftPanel().getMainFrame().getRightPanel().getTable().clearData();

後來發現工具列上也有個按鈕具有Clear Data的相同功能,所以也直接在這邊的listener裡寫上這一行程式。後來使用者在測試時回饋說清空Table的同時,Delete Data應該setEnable(false),這時候就直接在兩個listener(最好是還記得有兩個地方)內多加一行而形成:

getLeftPanel().getMainFrame().getRightPanel().getTable().clearData();
getLeftPanel().getMainFrame().getRightPanel().getDeleteDataButton().setEnable(false);

在趕時間的前提下,當然沒空在Right Panel上定義clearTableData()同時封裝這兩個動作,因而繼續讓重覆的程式碼散落在各地。

基於以上的快速撰寫方式,雖然讓我的工具程式在被壓縮到不合理的時程後還能如期完成,卻同時造就出一個內部脈絡極為複雜、等於已經僵化的系統。在快速開發的同時無法“完全遵循”某些設計準則,就會付出類似的代價。