您也是打痛苦混戰的高手嗎?
我喜歡下圍棋,雖然我沒有太多的時間精進我的棋藝。以前看棋譜或是看一些圍棋的故事時經常會看到一些敘述文字說某些圍棋高手是打混戰的高手,這是說圍棋局面愈亂擁有這種特質的圍棋高手就愈容易勝出。NBA也是一樣,每年我都和一些好友玩Fantasy NBA,也喜歡看NBA球賽,一些NBA好手也是打混戰的高手,這也是局勢愈混亂,比分愈接近的時候這些NBA球員就表現得愈好。
但是很不幸的,在軟件開發方面如果我們說某人是打混戰的高手,那麼我就不知道這是恭維還是暗諷。什麼是軟件開發打混戰的高手? 想想您寫過多少似曾相識的程序代碼? 許多軟件開發打混戰的高手在寫程序代碼時非常的快速,腦筋更是運轉得快得不得了,因此當他們需要一些程序代碼時,往往不願意花時間想想或是找找是不是已經有了可供使用的程序代碼,就很快的再寫了一次,因爲他們覺得再寫一次更快。或是當他們在寫COM+,.NET,Java時,許多中間的程序代碼都大同小異,只是因爲程序語言的不同或是使用的技術不同,就再寫一次。
另外的情形則不一定是軟件開發打混戰的高手,而應該說是一個團隊中的成員所發生的事情。例如:在一個軟件開發團隊中,A和B在寫自己的應用程序時,A和B都可能同時在撰寫類似的程序代碼,例如A可能寫了select * from employee,而B也正寫select e.ID, e.Name, … from employee e。或是A需要一個方法叫做GetSeminarQA以便得到研討會所有的QA問題,而B也在寫類似的方法,只是A使用Delphi程序語言,而B則使用Java。許多有對項目開發有經驗的人都知道,一旦當項目完成(或是完成不了)之後,如果使用Audit/Metric工具來檢查項目程序代碼,就會發現許多,許多類似的程序代碼不斷地出現在項目的不同的程序中。
也許重構正是因爲那些聰明的人在檢視了以往的程序代碼之後發現原來自己正是打混戰的高手因此而發展出來的技術。
混戰高手需要改變嗎? 我也不知道,各人愛好不同,讓我們看看下面的一個企業邏輯模型。DevCoSeminar類別定義了三個方法,其中的GetClosedQAs方法可以回傳一個研討會所有的QA問題,那麼我們應該如何實作GetClosedQAs呢?
如果這去問Java的朋友,他們會用Java + JDBC或是Java + Hibernate來實作,如果詢問C#的朋友,他們馬上會用C#+ADO.NET在數秒之內完成,Delphi? 那選擇更多了,Delphi+dbExpress,Delphi+ADO,Delphi+,,,。VB呢? PHP呢? Python呢? 嗯這個問題太簡單了,許多人也許不根本不屑一顧。
但是仔細看看上面的模型,不管使用Java,C#,Delphi或是其它的程序語言,上面的企業邏輯規則都是一樣的,不會因爲選擇的程序語言不同而改變,最多是實作方法不同的,那麼您也許會問然後呢?
讓我們換個角度想想,如果我們在企業邏輯模型中就完成GetClosedQAs方法呢?
看看下面的圖形,我在GetClosedQAs的Body特性值中直接使用了OCL來撰寫GetClosedQAs方法。爲什麼要使用OCL? 因爲這個企業邏輯規則是一樣的,不會因爲客戶端的程序語言,平臺或是技術而改變。
有了上面的企業邏輯模型之後,對於開發人員人說仍然必須搞清楚如何在客戶端使用企業邏輯模型,這不困難,讓我們愛揮一下技術人員的天賦,追根究底的往下挖。
一旦在企業邏輯模型中定義了GetClosedQAs之後,ECO會自動產生如下的程序代碼:
[UmlTaggedValue('Eco.Body', 'self.hasQA->select(closed)->asSet()')]
function getClosedQAs: System.&Object;
嗯ECO使用了.NET的屬性來定義DevCoSeminar類別的getClosedQAs方法。再往下看,getClosedQAs的實作如下:
function DevCoSeminar.getClosedQAs: System.&Object;
type
TArrayOfobject = array of &object;
begin
Result := (System.&Object(Self.AsIObject.Invoke('getClosedQAs', New(TArrayOfobject, 0))));
end;
嗯,getClosedQAs呼叫了IObjectInstance的Invoke來執行'getClosedQAs',回傳的結果是一個包含執行結果對象的對象數組,。而IObjectInstance有如下的定義:
public interface IObjectInstance: IObject
{
IObjectStateMachine StateMachine { get; }
object Invoke(IMethod method, object[] parameters);
object Invoke(string methodName, object[] parameters);
object Invoke(IMethod method, IElement[] parameters);
}
OK,很好,現在只要我們搞清楚如何擷取出getClosedQAs回傳的結果就可以取得依照企業邏輯模型定義的getClosedQAs方法的運算結果。下面的程序代碼就是使用Delphi做爲客戶端程序語言來存取執行的結果:
procedure TWinForm.btnQAs_Click(sender: System.Object; e: System.EventArgs);
var
aSeminar : DevCoSeminar;
anObject : System.&Object;
begin
aSeminar := Self.cmhSeminar.Element.AsObject as DevCoSeminar;
anObject := aSeminar.getClosedQAs;
HandleQAs(anObject);
end;
現在唯一的難題就是如何從getClosedQAs方法回傳的型態爲System.Object的結果值出運算結果。在這裏讓我給使用ECO的朋友一個暗示,對於像getClosedQAs方法執行OCL並且回傳結果對象串行的方法,這個回傳的結果都實作了ICollection接口,因此我們只需要把執行結果轉變型態爲ICollection接口就可以使用Delphi的for…in循環或是C#的foreach循環取得結果,例如下面的程序代碼就範例了從getClosedQAs的對象串行中取出所有符合條件的QA對象:
procedure TWinForm.HandleQAs(anObject: System.&Object);
var
aQA : QA;
begin
for aQA in (anObject as ICollection) do
begin
Self.lbResult.Items.Add(aQA.Question);
end;
end;
執行一下上面的程序代碼,我們就可以順利的得到正確的結果,下圖就是一個範例應用程序的執行結果畫面:
對於C#的朋友,程序代碼和Delphi是一樣的,關鍵在企業邏輯模型,任何的客戶端程序語言都可以重複使用企業邏輯模型中的設計:
private void button1_Click(object sender, System.EventArgs e)
{
DevCoSeminar aSeminar = cmhSeminar.Element.AsObject as DevCoSeminar;
System.Object anObject = aSeminar.getClosedQAs();
HandleQAs(anObject);
}
我還是要問,您感覺到了什麼? 是繼續讓獨自的開發人員在不同的程序代碼中仍然撰寫各自的程序代碼? 爲每一個類似的情形不斷的撰寫重複的程序代碼? 爲不同的程序語言不斷的撰寫重複的程序代碼? 爲不同的平臺不斷的撰寫重複的程序代碼? 我想這沒有標準的答案,每個人的風格不同,有人永遠是打混戰的高手,有人卻能夠在知道了新的選擇之後開始思考這些新的選擇會或是可以帶來什麼改變。
也許學習MDA/DDA/ECO的價值之一是開始讓我們思考新的選擇以及新的選擇可能帶來的影響是什麼!