2009年4月29日 星期三

U28 加強服務接受度的幾個特性

最近參加了服務體驗工程方法(SEE)的部分課程,其中一天提到服務接受度的分析,講師是一位認知心理學的助理教授,他從生活中觀察並分析許多事件的使用者行為,歸納出提供客戶服務時的一些原則與重點。

有一個段落的主旨是提高客戶的服務接受度,講師建議從以下幾個方向來加強:
●可理解性。接觸系統後能很快理解內部的運作邏輯。
●可學習性。接觸系統後能很快學會如何完成作業。
●效率。熟悉系統後可以很有效率地完成。
●可記憶性。隔一段時日後再使用系統還能記得如何操作。
●錯誤處理。系統應能避免犯錯,犯錯了也有機會回復

聽課的同時,我很認真地以這幾個特性來檢驗自己的設計想法。現在通常產出的程式碼並無規則可循,每個團隊都使用自己熟知的一套,看別人的程式時根本無從著手(除了極少數的天才),更不用說要理解、學習與熟用。

一方面為了統一分析與設計的基本準則,另一方面為了提升程度略差的人到達一定的水準,固定的方法與固定的產出是最容易讓理解、學習與使用門檻降低的方法。能力較好的人捨棄一些快速完成功能的捷徑,轉而鋪陳讓所有人共同提升水準的作法,才能夠讓開發團隊不致變調為英雄的工具而真的稱得上團隊。

為了往這個方向邁進,主管希望我把部落格的想法濃縮到投影片中,與公司裡資深的同仁們討論其可行性與最佳作法。把想法轉換為可執行的方法,這是我接下來的工作目標。

註:在設計且撰寫程式的同時,是否曾經想過程式碼除了達成功能之外的用途?看別人不同風格的程式碼會抱怨看不懂,自己寫出來的說不定事實上沒人看得懂;如果心裡只看到自己的開發目標,對其他面向的需要視而不見,終究只是無法提升團隊實力的自我格局而已。

2009年4月27日 星期一

U27 繞一大圈後再返回原點的收獲

之前看自己寫的或是別人寫的程式碼時,總感覺有一大堆不同意義的程式混雜在一起,但是又無法說清楚那些是什麼。隨著經驗的增長且接觸較多的理論後,才能夠明確地指出程式的佈置應有六種意義,同時在2007/10開始在合作的專案裡嘗試實作。然而又在2009/04發現最重要的Flow與Action實際上是可以放在一起的,只需要運用額外的註解。

為了印證自己的想法,最近特地去查看重構的方法(為了Class、Method的佈置)與敏捷開發的原則(為了快速的開發),並在心裡作了一些比較。

重構部分,我比較在意的是靜態佈置與資料、動作定位的問題。儘可能最大化的可能佈置Class結構可以避免Extract(Interface、Subclass、Superclass);設計方法時依照所處層級的動作意義對應放置可以不用Move、Extract(Method、Field);同時能夠減少Pull Up、Pull Down(Method、Field)有時必須連帶Extract Class的影響。另外貫徹絕對重用的想法就能根絕Duplicated Code與Shortgun Surgery。

至於敏捷開發,同步開發團隊思維與換人接手程式的原則,在定義固定的元件結構下甚至不需要pair programming就能夠直接換另一個成員;註解與文件是為了讓別人更瞭解自己的程式的原則,應用註解來產生全部Flow與Action的流程圖可以滿足;快速地不斷產生成果的原則,在軟體元件化後就能夠快速組裝流程甚至運用SA Tool來作勾選與拖拉式的開發;設計是為了駕馭客戶需求的改變,可以用任意tier、layer的切割、快速的Flow調整與更換不同的軟體元件來滿足。

敏捷開發加上重構,看起來是用敏捷的寫作方式做出可執行的成果,再重構程式碼調整Class、Method與Data的佈置作最佳化。既然如此,為什麼不用敏捷開發直接作出重構後應該有的佈置呢?目前看過的方法論裡文件都與程式脫節必須另外製作,融合各家所長以理想的程式結構為根本,直接使用程式來設計,再應用註解自動化產生絕大部分的設計文件,是不是可以節省製作文件的時間同時又保證其正確性?

目前只是一個連理論都不甚清楚的程式寫作員,但是相較於其他工程領域都有固定的模組化方式來堆砌出客戶想要的成果,軟體工程卻獨缺能夠被大部分人同意的決定性作法。不知道未來的程式設計是否有機會往我現在努力的方向演進?

2009年4月25日 星期六

U26 SOA的參考模型(Solution Stack)

近年來參加了幾次SOA的課程,直到最近的一次我才終於聽懂大部分的內容。其中參考模型的結構完全可以用我使用的切割單位來遞迴對應解釋,因而對這個部分立即開竅。(大的切割單位概稱為元件,內含三個小的切割單位:入口、流程與動作)

SOA參考模型的縱向結構是Consumers>Business Process>Services>Service Components>Operation System。從上往下可以切割為三個大的切割單位,內部再各自包含三個小的切割單位;每一層大的切割單位各自以不同的標準放到不同層級的容器裡執行,並加以不同層級的監控與加解密功能。
●功能:Consumers>Business Process>Services
 入口:Business Process Interface
 流程:Business Process
 動作:Business Process與Service Interface
●服務:Services>Service Components
 入口:Service Interface
 流程:Service內部
 動作:Service Interface與Service Component Interface
●元件:Service Components>Operation System
 入口:Service Component Interface
 流程:Service Component內部
 動作:Service Component Interface與Operation System

將階層式重覆發生的事情設計為遞迴呼叫的方法是好的方法,理由是把重限層次可能發生的事簡化為一層。同樣地我相信將階層式重覆存在的結構用遞迴方法說明其精神也是好的比喻,原因是把原本要分開記憶的多個單位簡化成一種。

同樣作用的程式碼重覆存在是重構時要消滅的現象,在雜亂的現象中抽取出唯一的說明邏輯會有較好的學習效果。在任何場所、任何應用上推行極致的“絕對重用”會是增進學習與使用效率的良好方法,但是要注意維持唯一存在時必然會耗費額外的努力。

2009年4月24日 星期五

U25 再看簡單型的Component

以往的Method註解用途只用來存放說明、參數、傳回值與例外,在這個情況下方法意義的區分(指給處理程式用的)只能靠Class的佈置來達成,但是現在的註解已經可以盛裝更大量的資訊,我們就可以將原先分為六大部分其中的流程控制類(Flow與Action)再回歸為一個Class──只要能夠在Method註解裡區分其意義即可。

在元件裡流程控制相關的方法類型有以下五種(第一種放在Implementation),在註解裡定義一個方法類型欄位就能在處理程式裡取得對應值。
●Implementation Method(入口方法)
●Flow Method且被Implementation Method直接呼叫(被入口方法呼叫的流程方法)
●Flow Method但只被Flow Method呼叫(內部流程方法)
●Action Method且被Flow Method直接呼叫(被流程方法呼叫的動作方法)
●Action Method但只被Action Method呼叫(內部動作方法)

元件設計回歸到這樣的簡單結構在設計上已經很接近現在的主流作法,製作流程方法時毌須浪費時間在與結構佈置有關的行為,唯一需要作的是依註解規定撰寫應該有的註解內容──然而這本來就是應該做的事。至此,我們能夠在不多花費設計時間的前提下完成滿足以軟體工廠為前提的元件設計。

我會為了容易編輯註解的目的製作一個編輯器,除了可以把註解內容(多行文字)直覺地以UI欄位再編修之外,還可以詳細定義註解所要編輯的屬性並檢查值是否合法,設計人員只需要依畫面提示輸入註解內容而不用去注意註解的格式,便能得到符合規定的註解內容。另外還需要改寫Java編輯器,增加註解可收合或展開的功能,不然混雜著一大堆的程式碼雖然容易看懂卻也很難看清楚。

註:有些元件的Flow會因客戶的需要而有很大的不同,這種元件一定要用結構型的方式設計。因為唯有明確地知道Action、Model與Properties的全部範圍才有可能作最佳的設計,同時避免掉繼承後override多次後的混亂。另一種類型像基本Data Model讀寫器那樣Action有不同的實作,這也應該將Flow與Action分開放置。

2009年4月22日 星期三

U24 根本的建立(9)──註解Data Model

截至目前需要定義的註解類型有以下幾種:
●資料檔案裡的註解。這裡討論的是應用在基本Data Model編輯器所用的定義註解。
●Java Doc註解。應用在程式碼的Class、Attribute與Method的說明。
●Java Comment。Java的標準註解,用以說明Method內部的程式區塊。
●每個知識庫資訊的說明附帶的描述內容。

目前Java程式可以使用的註解有Java Doc(Class與Method)、annotation(Method)與Comment(Method內部)三種,我選用作為註解Data Model使用的是Java Doc與Comment。

這裡應該依註解全部的應用可能來決定註解Date Model通用的方法,並依得到的結果於基本Data Model中再定義CommentDateModelInterface來對應。所有註解若源自同一根本的CommentDataModelAbstract,就表示可藉由對應的parser在各種實際註解中作任意格式的轉換。

檢視現在的基本Data Model,其具有的元素與註解的對應如下:(注意:程式的Java Doc與程式本身在轉換工具裡是分離的)
●name:沒有使用。
●value:描述內容,格式為字串。
●comment:沒有使用。
●attributes:存放與註解關聯的實際物件鍵值,以key-value的方式存在。匯出時會放置到與property同層級的地方。
●children(properties):註解專用的欄位,以key-collection的方式存在。因為註解欄位的內容有數種意義,所以應該定義為CommentFieldDataModelInterface放在集合裡。

應該可以發現BeanDataModel是CommentDataModel的子集合, workspace在此加入CommentDataModel並修改BeanDataModel的繼承關係(BeanDataModel提供基本的properties操作而CommentDataModel改寫之);另外要準備CommentFieldDataModelInterface作為CommentDataModel的屬性方法參數(屬性的字串值要在內部轉換為CommentFieldDataModelInterface物件)。

在這裡定義的是可應用於各種註解的通用資料物件,未來為了符合四種註解型態必須要再延伸定義四種註解Data Model與四種註解Data Model讀寫器配合實際的應用。

2009年4月20日 星期一

U23 將程式碼視為Model,產出文件視為View

“這個功能的流程大概是什麼樣子?”“功能實作時呼叫了哪些元件的方法?”……,之前詢問快速完成功能的同事時,都會得到很經典的一句:“你要的東西全部都在程式碼裡,100%正確而且100%可以正常執行!”。所有人都同意東西全在程式碼裡,但是其他人要花多少時間才能取得自己想要的資訊?

一個功能使用UML來描述時至少需要四張圖(Use Case、Activity、Class、Sequence),畫這些圖的時間遠大於程式寫作的時間,更何況未來還需要另外製作各種使用的水平與垂直追溯表!額外花費心力完成一堆文件後,日後遇到程式碼的修改還得同時修改一大堆文件,這樣的工作內容有誰可以接受?要我也不想這麼做。

既然程式碼裡有完整且正確的所有資訊,又同時需要數份描述那些資訊的文件產出,那麼應用MVC Design Pattern設計一個轉換工具(Controller)把程式碼的內容(Model)轉出為各式各樣的對應文件(View)不是很理想嗎?文件可以制訂出固定不變的格式,規定程式碼的寫作格式有助於固定住轉換來源,兩者是達成功能的必要條件。

即使固定了設計的結構,但是不同程式語言的解析方法不一樣,每一個人對於同一種語言的寫作方式也不同,要寫出包含各種不同寫法的轉換工具近乎於不可能;我所能想到的就是在依設計準則安排Class與Method的佈置,同時再定義出固定不變的註解格式來提供轉換工具讀取(讓人快速看懂也是目的)。

Class與Method的佈置提供的是轉換工具處理的流程,註解內容則是轉換工具處理的動作,這兩類規格的定義讓程式碼與文件得以作有關聯的轉換(甚至可以做到註解的雙向轉換)。實作的重點在於註解Data Model的統一定義,這正是接下來要進行的主題。

2009年4月17日 星期五

U22 根本的建立(8)──知識庫的維護功能

使用,是根據現有物件的放置方式加以運用,但是有用的物件要存放到指定的位置需要將原本散落在各種文件裡的有用內容收集到指定的存放位置。在有用物件與儲存庫之間的關聯,就產生了各種維護功能的需求。

“從各種文件裡取得有用的資訊放入知識庫”是第一個維護需求。這個需求要成立的前提是必須能夠定義總共有哪些文件與每份文件的格式,CMMI或ISO導入的可以有效地在分析開發流程的同時固定所有產出文件的數量與格式。我們可以想像若是文件的數量與格式不停地變動,這個需求根本不可能完成。另外收集的資訊應註明作者與出處以便未來可以追溯來源,因此在收集資訊的同時也要額外收集這些欄位。

“資訊放入知識庫前需要被review”是第二個維護需求。如同程式碼在放入儲存庫前有同步比較差異的功能,收集的資訊也應該有類似的機制加以比較篩選來避免重覆或是無用資訊的問題;這也表示我們需要一個資訊暫存區與比較的設計。現在網路上有些討論區或留言版由於無法過濾內容且採不記名方式,因而存在了非常多的垃圾留言,這種現象一定要避免。

“每個版本的產生要追溯出有哪些變動”是第三個維護需求。就像標準的文件異動時要額外列出內容變動列表、系統更新版本時列出程式異動清單那樣,知識庫的維護也應該要有類似的記錄。維護記錄也必須具備不同的查詢方式以滿足針對版本、時間、作者、來源……等等不同面向的查詢。

“每個單位將有用資訊集合為中間檔提交review”是第四個維護需求。公司進行的專案可能四散在不同場所(全球化的公司也會如此),在資訊的同步必然會有衝突或是連線上的問題。讓每一個開發團隊都能事先抽取要進入知識庫的內容交給特定單位同步與review,會有集中處理的好處;而且具備中間檔的好處是在未來可以任意變動資料的來源卻只有很小的影響。

一切聽起來都很理想,不過從文件抽取內容很簡單,但是該如何從程式碼裡抽取有用資訊呢?或者說,因為程式碼的變動性過大,我們應該從程式碼裡抽取資訊嗎?

2009年4月16日 星期四

U21 根本的建立(7)──知識庫的使用功能

反應快的人主要是因為接觸到的知識被“記憶”、被“分類”、且有“適當的關聯”,但是這對於公司而言這只有少數人能具有,況且每個人的記憶都分散而難以同步、使用。對一個開發團隊來說,知識庫就像是將大家腦中的知識聯集起來作組織化的存放,同時又要讓每一位成員快速地找到自己想要的資訊。由此可知知識庫若應用到極致將會是多麼有生產力。

“將所有的資訊作組織化的存放”是第一個功能需求。談需求時我們總希望客戶直接提供百分之百的詳細規格,但設計知識庫卻不可能有這種東西。依建立的日期時間依序存放是比較理想的方式,而且所有的資訊只放在一個表格裡;雖然建立一些分類分開存放也是不錯的點子但是那都可以藉由關聯作出分類,同時“唯一存在”的儲存位置也較易於管理。有存放就會有CRUD連帶需要的子功能。

“儲存的資訊有最適切的關聯”是第二個功能需求,這也沒法列出詳細規格。如同關聯式資料庫的表格定義,每一種關聯都應該為之建立一個專屬的欄位,在分類搜尋時才能找出符合條件的集合。知識庫要具有哪些特性、各要怎麼定義才會使所有資訊之間有最適切的關聯?這是相當值得研究的課題。

“能迅速找出符合條件的所有資訊”是第三個功能需求。存放與關聯都作過良好定義的話,這個需求看來只需要在UI的使用便利性下功夫就會有良好的成效。“全文檢索”是另一個好用的功能,但考量到技術層面倒也不是非做到不可。

“每一則資訊的變更歷史”是第四個功能需求。可追溯的變更是重要的潛在需求,在查到資訊的同時應能另外顯示該則資訊歷來的編修記錄;感覺起來很像是程式碼的儲存庫功能。這對使用者來說並非必要,但是對於維護功能卻需要藉此追溯記錄。

註:同事說部落格裡的內容實在太多,應該安排更容易上手的分類。若把部落格視為知識庫,每篇文章依照日期時間逐一放入到這唯一的儲存區;目前欠缺的就是依照學習需求分類的關聯索引,這也表示所以文章的標籤必須加以重新編排。

2009年4月15日 星期三

U20 開發團隊必要的絕對重用──知識庫

在公司裡與設計有關的討論裡,我一直堅持著“絕對重用”的原則。除了程式裡變數與方法使用的唯一化之外,避免想法散落在各人腦中在需要時卻無法取得最新狀態的問題而必須建立的知識庫,也是我希望建立的制度。當然,程式碼與知識庫之間同樣應該有自動化同步內容的機制才不至於造成同仁額外花費時間去做,能夠架構在二者之間的橋樑就只有程式的註解。

開發時會存在的絕對重用有:依設計準則所規定的統一元件結構、提供編輯器的一致註解、收集全部有用資訊的知識庫。現在元件結構已經確定,接下來需要的是繼承自基本Data Model的註解Data Model(有程式註解與檔案註解兩種,後者是提供通用編輯器使用的),還有放置從程式註解Data Model取得資訊的知識庫。

曾在iThome網站看到這麼一篇文章(重新思考知識管理,http://www.ithome.com.tw/itadm/article.php?c=52119),這與我心中的理想藍圖不謀而合。對一般使用的知識庫而言,規定資料文件的編排格式再從固定的位置抽取有用的資訊同步進來是較為容易的;但是對於程式碼而言,要能用通用程式抽取有用的資訊的話,勢必要規定所有程式碼一致的編排方式與寫法才能辦到。千變萬化的程式設計與實作寫法是達成這個功能的最大難處,也唯有嚴格規定產出結構與註解格式才有辦法符合知識庫的基本要求。

根據這樣的理想,產生了以下的工作項目等待實現:
●註解內容與基本Data Model的相互轉換
●知識庫的建立與查詢與建立版本功能
●註解內容與知識庫內容比較與匯入
●基本Data Model的註解轉換為基本Data Model
●使用基本Data Model編輯器建立程式註解格式編輯工具

2009年4月13日 星期一

U19 第三個元件(3)──基本Data Model編輯器的存取

畫面呈現的作法確定之後,就只剩下建立Data Model與編輯器的對應關係。在使用上的設計有幾個簡單的方向:整個Data Model對應一個頁面或多個頁面的存取、對於欄位內容值的存取與屬性變更等。

  public boolean setDataModelValue(ModelParserModelInterface model);
  public boolean getDataModelValue(ModelParserModelInterface model);

  public Object getValue(String fieldId) throws IDNotFoundException;
  public boolean setValue(String fieldId, Object Value) throws IDNotFoundException;
  public Object getProperty(String fieldId, String propName) throws IDNotFoundException;
  public boolean setProperty(String fieldId, String propName, Object Value) throws IDNotFoundException;

  public Object getValue(String pageId, String fieldId) throws IDNotFoundException;
  public boolean setValue(String pageId, String fieldId, Object Value) throws IDNotFoundException;
  public Object getProperty(String pageId, String fieldId, String propName) throws IDNotFoundException;
  public boolean setProperty(String pageId, String fieldId, String propName, Object Value) throws IDNotFoundException;

以上是大致需要的介面方法定義。UI的佈置會以一個Data Model對應一個頁面為主,在多個bean file的場合下會有JTree的對應佈置,在頁面的存取規則定義pageId來指定取得實際使用的那個;資料放入UI時會以bean id來命名,方法內傳入fieldId即可定位實際存取的欄位。

說真的,基本Data Model編輯器若是不做也沒什麼影響,因為對IT人員來說直接開啟檔案編輯內容是又快又簡單的事,但是對一般使用者來說卻相對地很困難,尤其在尋找想要修改的內容或是輸入XML的標籤有錯誤時根本無法達成。要能快速地清楚所有可以修改的設定與其代表的意義,同時限定與檢核輸入值的正確性,這些好處就只有基本Data Model編輯器可以快速提供。

註:這裡的內容並未實作在workspace裡,但是在之前的專案裡已驗證過可行。

2009年4月10日 星期五

U18 第三個元件(2)──基本Data Model編輯器的層次

如果一個編輯器只能處理一個bean是非常浪費資源的,更何況一個bean file裡可以有多個bean,一個設定資料夾裡可以有多個bean file。folder-bean file-bean-property的完整排列需要三維陣列,整個系統定義使用的設定檔都放置到同一個編輯器是這個元件的最終需求。

bean-property的對應如同上一篇提到的會由JPanel-Jcomponent來實作;folder-bean file-bean會選用JTree作兩層式的管理,bean file列示在第一層節點,bean則依附在對應的bean file之下。這樣的設計即使在面臨只開一個bean file的場合或是系統同時有多個設定資料夾,也只需要對應縮減或增加JTree的結構層級就可以符合需求。

回到屬性的編輯內容來看,讀取設定檔的內容與註解只能列出現在有定義的屬性,總計若有20個屬性而設定檔內只有10個有儲存值的話(有些不能放值以便有其他作用),目前的編輯器沒有辦法列出那10個不存在的屬性。解決這個問題必須另外定義一個檔案放置所有的屬性與UI定義,編輯器根據該頁面定義檔建立其專用的編輯畫面。這才是編輯器的完成型態。

提供便利的編輯器給使用者快速地改變所有設定檔的內容,設定檔可編輯的屬性同時能夠方便地更動,這兩種功能的組合就是SA Tool所需要的基本編輯界面。藉由檔案、頁面與屬性內容的擴充,SA Tool才能夠具有彈性地擴充需要的屬性來對應開發系統所需要的參數定義,之後再經由統一的元件結構與用法處理讀入的Properties調整元件的實際作用。

SA Tool需要的編輯器在UI上還需要另外定義其他與顯示無關的設定,像是判斷值開關其他屬性是否可以編輯、便利編輯的屬性群組化、外掛檢查輸入值的檢查類別……等等,這些都可以因應編輯的必要性予以加強。但是基本Data Model編輯器本身已經不容易實作了,往上堆疊設計的SA Tool自然更為複雜,而且還要搭配對應的開發方法論才能定義出適切範圍的正確屬性。

註:這裡的內容並未實作在workspace裡,但是在之前的專案裡已驗證過可行。

2009年4月8日 星期三

U17 第三個元件(1)──基本Data Model編輯器的頁面

當只存在一種Data Model時,就有機會為之定義通用編輯器提供給使用者快速編輯內容。編輯的方式有兩大類:一種是根據資料屬性的範圍修改各自的值,另一種是除了修改屬性值之外還允許增減不同名稱的屬性。前者以PropertiesDataModel的設定特性而後者是以BeanDataModel的屬性特性作為設計基礎;PropertiesDataModel可以視為不能變動屬性的BeanDataModel,定義設定檔時一定要使用bean file的方式才能兼顧未來的擴充性。

最底層的設計是將一個可定義的屬性對應到一個UI Bean,利用comment來標示這個UI Bean的實際類別與描述屬性;UI類別名稱會依序生成,同時呼叫描述屬性的對應方法(有些屬性需要加工處理,像是Jcombo的選單內容字串)。基本的定義屬性有:提示文字、生成類別、enable、visible與選單內容字串。提示文字與編輯欄位的tooltip都會顯示屬性原本的名稱。

欄位向上的集合是頁面,對應的節點是bean;允許定義的屬性是提示文字、生成類別與欄位寬度。頁面的寬度減去欄位寬度就是提示文字的寬度。再向上就是根節點beans,允許定義的屬性是提示文字、background、foreground、font、width與height。加上單頁式的編輯註解後,設定檔案的內容會像是下面的樣子,附圖是執行時的範例。

<!-- frame's name.
  class=javax.swing.JFrame,background=0x0000ff,foreground=0xffff00,
  font=monospaced-plain-16,width=400,height=300 -->
<beans>
  <!—Page 0.
    class=javax.swing.JPanel,fieldwidth=200-->
  <bean id="FieldModel">
    <!-- This is field1 prompt.
      class=javax.swing.JTextField,enable=true,visible=true -->
    <property name="field1" value="default1" />
    <!-- This is field2 prompt.
      class=javax.swing.JCheckBox,enable=true,visible=true -->
    <property name="field2" value="true" />
    <!-- This is field3 prompt.
      class=javax.swing.JComboBox,enable=true,visible=true,items=|1|2|3 -->
    <property name="field3" value="" />
    <!-- .
      class=javax.swing.JComboBox,enable=false,visible=true,items=|1|2|3 -->
    <property name="field4" value="2" />
  </bean>
</beans>


編輯欄位除了字串的設定外,還應該為list、map與listmap準備另外用對話盒放置的集合編輯器才算擁有完整的編輯功能。

註:這裡的內容並未實作在workspace裡,但是在之前的專案裡已驗證過可行。

2009年4月6日 星期一

U16 元件各自的Package與Properties設定

使用結構設計的元件最好是一個Package內只放一個Component,主要的原因當然是由於每個完整Component裡至少會有20個檔案,混雜了兩個以上元件的Package根本會令人不知道裡面的程式到底誰是誰的。而更重要的是多個元件對應到一個資料夾是多對一的關係,倘使元件的放置需要加以切割又會面臨到重組的問題。

有一個觀念必須釐清:並不是所有的功能單位都要設計為Component,有些實作適合以Class的形式存在(像是LogUtil與UI Bean),而包含較多邏輯性內容的功能偏向於包裝為Component(像是ApplLog)。Component會需要傳入參數來因應不同狀況下的處理方式,參數的來源有兩大類:呼叫方法時傳入(執行動作的參考狀態)與定義在元件用的參數檔(一般情況下的通用狀態)。

建議使用Bean File的形式來存放多個Component的參數設定,一個bean對應到一個Component設定並將ComponentID放到id的屬性裡。BeanFileDataModelInterface增加一個方法,在系統初始化時取出元件的Properties後放到所屬的Component以完成元件的初始化。
  public void getBeanProperties(String beanId, BasePropertiesInterface properties) throws BeanNotFoundException;

ApplLog元件使用程式初始化時需要有這些程式碼:
  ApplLogProperties properties = new ApplLogProperties();
  ((Properties) properties).setProperty(BasePropertiesInterface.CLASS_FLOW, "tw.idv.joying.component.appllog.ApplLogFlow");
  ((Properties) properties).setProperty(BasePropertiesInterface.CLASS_ACTION, "tw.idv.joying.component.appllog.ApplLogAction");
  properties.setLogLevel(LogUtil.INFO);
  ApplLog.getInstance().setApplLogProperties(properties);

將參數改存放到XML檔案會是這樣的形式:
<beans>
  <bean id="tw.idv.joying.component.appllog.ApplLog">
    <property name="classFlow" value="tw.idv.joying.component.appllog.ApplLogFlow" />
    <property name="classAction" value="tw.idv.joying.component.appllog.ApplLogAction" />
    <property name="logLevel" value="4" />
  </bean>
</beans>

針對ApplLog的初始化可以改寫成這樣:(ModelParser針對不同的檔案讀取設定可以包裝成一個通用API,只要傳入檔名與處理類型就能拿到資料物件)
  ApplLogProperties properties = new ApplLogProperties();
  beanFile.getBeanProperties(ApplLogInterface.COMPONENTID, properties);
  ApplLog.getInstance().setApplLogProperties(properties);

最後可以改寫初始化的程式碼,不再需要每個元件都準備自己的設定檔,只要使用固定的初始化方式就能夠集中管理所有元件的設定。處理原則在初始化元件時取得beanId,由於命名方式一致可以捨棄new的寫法而改用Class.forName()取得元件的Properties物件,並傳入到元件裡作初始化。如此一來也統一了元件的初始化設定功能。

註:這裡的內容並未實作在workspace裡,但是在之前的專案裡已驗證過可行。

2009年4月3日 星期五

U15 第二個元件(5)──讀寫Data Model的單元測試

由於workspace裡只實作了XML、Text與Properties三種資料類型,單元測試也只做好這三種測試。測試的檔案放在junit/settings/datafile資料夾下,測試的結果如下圖:(save方法的測試是寫檔後再讀回為data model測試內容,並沒有實際測試)

此時再回頭看基本Data Model相關的package下的所有檔案,雖然裡面有39個檔案,但是現在來看應該每一支程式的功用是什麼都一目瞭然。(配合U10的Class Diagram可以更快進入狀況)

元件庫現在的結構圖:

2009年4月2日 星期四

U14 第二個元件(4)──讀寫Bean與Object檔案

<beans>
  <bean id="bean1" class="BeanClass1">
    <property name="prop1" value="value1"/>
    <property name="prop2">
      <list>
        <value>value2-1</value>
      </list>
    </property>
    <property name="prop3">
      <map>
        <entry>
          <key>
            <value>key1</value>
          </key>
          <value>value1</value>
        </entry>
        <entry>
          <key>
            <value>key2</value>
          </key>
          <value>value2</value>
        </entry>
      </map>
    </property>
  </bean>
  <bean class="BeanClass2" id="bean2">
    <property name="prop" value="value"/>
  </bean>
</beans>

在這個通用的XML結構裡,隱含了beans與bean各自對應的BeanFileDataModelInterface與BeanDataModelInterface。前者負責的是對所有bean的管理功能,後者提供的是每一個bean內部的property存取與管理,以及id、class的存取方法。同時對於bean裡的tag也必須全部定義對應的常數。

  final static String NODE_NAME = "bean";
  final static String NODE_ATTRID = "id";
  final static String NODE_ATTRCLASS = "class";

  final static String PROPERTY_NAME = "property";
  final static String PROPERTY_ATTRNAME = "name";
  final static String PROPERTY_ATTRVALUE = "value";

  final static String VALUE_NAME = "value";
  final static String LIST_NAME = "list";
  final static String MAP_NAME = "map";
  final static String MAP_ENTRY = "entry";
  final static String MAP_KEY = "key";
  final static String LISTMAP_NAME = "listmap";


BeanDataModelAbstract繼承自XMLDataModelAbstract,由於bean的child都會以property的形式存在,因此全部property相關的方法事實上都是對child操作。另外在property的物件定義上除了原本的list、map基本類型之外,再加上自己特有的listmap以符合不同的需求。BeanParserActionAbstract可以繼承XMLParserActionAbstract再改寫對property存取的部分。

BeanFileDataModel與BeanDataModel可以衍伸出一種極重要的變型:ObjectDataModel,它會依照bean裡class屬性定義的類別名稱創造出該類別的物件實體。實作時物件應繼承基本Data Model以擁有底層所規定的一切特性,同時在自有的介面上具有符合上層想要操作的所有動作內容。這個作法實現了所有資料物件的根源都來自同一個底層Data Model的理想。

註:這裡的內容並未實作在workspace裡,但是在之前的專案裡已驗證過可行。

2009年4月1日 星期三

U13 第二個元件(3)──讀寫CSV與Excel檔案

TableDataModel顧名思義就是要處理表格型的資料。應用到實際存放搜尋的資料庫內容時,比起ResultSet會佔住資料庫的資源,或是取得ResultSet後再放到String[][]在取用資料時不確定在哪裡,TableDataModel提供了方便的存取介面且易於改寫內部;物件轉換為字串後再壓縮也可以節省網路傳輸的頻寬。

第一個子節點只存放欄位的名稱與欄位的型態而不存放資料(我常用的設定是將所有欄位型態都定義為字串),名稱用逗號分隔放在放在一個attribute;往下依序每個子節點都是一筆記錄,記錄裡的每個欄位值都使用欄位名稱放在attribute裡。實際的對應範例如下圖所示。


TableDataModelInterface定義了一些存取欄位名稱與值的方法,實作就根據資料在基本Data Model裡的放置而設計。再往上應該準備CSVParserAction與ExcelParserAction兩個存取檔案的類別,前者依逗號分隔規則切割資料,後者使用JXL函式庫來存取Excel檔案內的資料;在資料庫的select應用場合可以解析sql command內的欄位名稱,直接new TableDataModel後放入欄位名稱與搜尋的結果。
  public int getFieldCount();
  public String[] listFieldNames();
  public boolean setFieldNames(String[] fieldNames);
  public int getRecordCount();
  public String[] getRecord(int index);
  public boolean appendRecord(int index, String[] data);
  public boolean removeRecord(int index);
  public String[] listFieldValue(String fieldName);
  public String[] listFieldValue(int fieldIndex);
  public String[][] listTableValue();

如果對於這種寫法的速度覺得不滿意,當然可以改寫為更有效率的作法而不影響這個元件的設計。

註:這裡的內容並未實作在workspace裡,但是在之前的專案裡已驗證過可行。