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()同時封裝這兩個動作,因而繼續讓重覆的程式碼散落在各地。

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

沒有留言:

張貼留言