如何降低軟件開發維護成本、提高開發效率、減小需求變更對系統的影響以及延長系統的生命周期是每個軟件開發者都在思索的問題。然而傳統的開發途徑中上面的問題總是很難完美的解決。 MDA(模型驅動架構,Model Driven Architecture)是由OMG組織提出的新的軟件開發架構,他相對於傳統的開發方式有了很大的變化,軟件開發的驅動力不再來自於傳統的概要設計、編碼,而是由模型來驅動開發。使用MDA開發團隊可以將時間和精力集中在應用的業務邏輯上,而不需要花費大量時間來設計架構。MDA不僅僅是一種開發架構,更主要的是一種方法、標准,它獨立於系統平台和開發語言之外。各個軟件供應商也都相繼提供了MDA的解決方案,本文所講的ECO(Enterprise Core Objects)就是其中之一。
Borland在2004年初推出的Delphi 8 for Microsoft .NET Framework稱得上是Delphi歷史上最重要的一個版本,這個版本能否成功關系到Delphi能否在.net平台上再現win32平台上的輝煌。Delphi在win32平台上的成功很大程度上要歸功於優秀的VCL架構。正是因為微軟在Win32平台上沒有提供完整易用的組件庫,才使得VCL有如此蓬勃的發展。而在.NET平台上微軟已經提供了完善成熟的Framework,Delphi8中雖然依舊包含VCL,但現在的VCL.NET已經不能完全和.NET Framework的Winform兼容了,而是為了方便Delphi的用戶平滑過渡到. NET。失去VCL的優勢對於Delphi來說也並非完全是件壞事,Borland會把更多的精力放在如何提高開發工具的生產力上。Borland在收購TogetherSoft、BoldSoft、Starbase等公司後,開始對所掌握的技術進行整合,ECO正是Bold基礎上發展出的.NET平台MDA解決方案。它提供了封裝有各種典型業務應用的底層應用服務,可以很好的完成各種系統開發和業務應用開發。Borland軟件產品的副總裁,Boz Elloy認為,"ECO不但可以減少編寫和維護的代碼量,同樣可以減少風險,支持更高的軟件產品質量,我們相信ECO至少要比市場上的同類產品領先一年。"下面我們就實際體驗一下MDA在Delphi中帶給我們的驚喜。
實戰ECO開發
下面我們要開發的是一個汽車銷售客戶管理系統,汽車銷售商要管理客戶的資料,客戶中既有個人客戶也有企業客戶,兩者的屬性各有不同。汽車的型號資料和每輛汽車的銷售記錄同樣也要管理。根據上面的簡單需求來開始我們的ECO開發之旅。
1. 使用ECO Application Wizard建立一個工程
1) 打開Delphi 8 架構版在點擊File →New →Other打開新建項目對話框。選擇ECO Windows Application後點擊OK
2) 輸入工程名稱和路徑後點擊OK,ECO Application Wizard為我們創建了所需要的工程單元文件。(注意:因為Delphi 8 Eco中存在的一個BUG,在工程路徑或工程名中存在全角字符會引起編譯器編譯是發生錯誤,這裡必須保存在英文路徑下)
ECO Application Wizard生成的新工程包含以下幾個文件:
文件名 描述 CoreClasses.pas 其中包含UML packages、 interfaces、 classes之間關聯關系的源代碼和在模型中定義的類型 EcoCRMEcoSpace.pas 由Borland.Eco.Handles.EcoSpace派生出的子類TEcoCRMEcoSpace的源代碼 WinForm.pas 應用程序主窗體的源代碼 Borland.Eco.Core.dll
Borland.Eco.Handles.dll
Borland.Eco.Interfaces.dll
Borland.Eco.Ocl.ParserCore.dll
Borland.Eco.Persistence.dll 這些文件是ECO applications運行時所需要的文件,存放在C:\Program Files\Common Files\Borland Shared\BDS\Shared Assemblies\2.0目錄下
2. 需求分析
由上面所提供的需求我們可以提煉出以下幾個類:
名稱 描述 Customer 客戶,包含屬性有:Name(名字)、Phone(聯系電話) Address(住址) Person 個人客戶,除了包括客戶的屬性外還有Sex(性別)屬性 Company 公司客戶,其具有Customer的全部屬性 Car 汽車,包含的屬性有ID(汽車編號) Name(汽車名稱) CarType(汽車型號) CarSell 汽車的銷售記錄,包含的屬性有BuyDate(購車時間)Fee(價格)
各個類之間的對應關系如下:
Person和Company都是Customer的子類,它們都擁有Customer的屬性。汽車的銷售記錄中包含所銷售的車輛,CarSell和Car有一對一或一對多的對應關系,也就是一條銷售記錄可能對應多個車輛,同樣客戶和銷售記錄也有一對一或一對多的對應關系。
3. 設計模型
根據上面的分析結果我們可以開始從ECO中進行建模,首先選擇模型視圖,打開CoreClasses包,然後雙擊CoreClasses打開模型設計面板。
Tool Palette 中的UML Class Diagram 中共包含6個組件他們分別是:
名稱 描述 Eco Package 模型包,如果系統結構復雜可用其來進行分解以降低復雜度,CoreClasses包是ECO Application Wizard默認生成的包 Class 類 Generalization/Implementation 繼承關系 Association 聯合關系 Note 注釋 Note Link 注釋鏈接
首先我們將一個Class拖放到設計器中,將其命名為Customer,再點擊右鍵選擇 Add →Attribute為其添加一個屬性,並設置該屬性的名稱為"Name"類型為String。在屬性的類型設置中,你可以輸入Delphi支持的類型。同過設置Alias屬性還可以在模型中顯示中文別名,方便我們進行建模。Class常用的屬性如下:
屬性名 默認值 描述 Abstract False 抽象類 Sealed False 能否被繼承 Alias '' 別名,顯示在模型編輯器中的名字 Persistence persistent 是否被存儲 DefaultStringRepresentation '' 默認顯示字符,可將其設置為關鍵的屬性名
根據我們在上面需求中所分析出的四個類在編輯器中創建,因為Person和Company都是繼承自Customer,所以僅僅描述其不同於Customer的屬性即可。完成後設計器內容如圖所示:
完成上述工作後,我們開始對模型間的關聯關系進行設計,首先來完成Customer的繼承關系。選擇Tool Palette中的Generalization/Implementation組件,點擊Person模型後拖曳到Customer模型去,這樣就建立好了兩者間的繼承關系,同樣建立Company與Customer的繼承關系。
然後開始建立Customer和CarSell之間的主子對應關系,選擇Tool Palette中的Association組件,點擊CarSell模型後拖曳到Customer模型去,即生成了一個Association。點擊選中Association後設置它的屬性,修改其name為PayAssociation,End1的 Multiplicity為0..*;End2的Multiplicity為1。Multiplicity屬性用於控制模型間的連接關系,其可選屬性的含義為:
可選擇值 描述 0..1 有零個或一個連接 1..1 有一個且只有一個連接 0..* 有零個或多個連接 1..* 有一個或多個連接同理為CarSell和Car建立關聯關系,建立完成後的模型圖如下:
到此為止我們的建模工作已經完成,在此之後我們還需要把對象保存發布到數據庫中,這就用到了Eco所提供到的PersistenceMapper組件。在Delphi 8 中Eco一共提供了13個組件,它們分別是:
編號 組件名 命名空間 功能 1 CurrencyManagerHandle Borland.Eco.Handles 用來操作當前游標所在的對象實體 2 ExpressionHandle Borland.Eco.Handles 可以將其簡單理解為一個數據源組件(TDataset),數據源通過SQL語句向數據庫查詢記錄,而ExpressionHandle更為靈活可控的OCL語句查詢對象實例 3 OclPSHandle Borland.Eco.Handles 根據OCL表達式得到元素值,其主要方法為Execute 4 OclVariables Borland.Eco.Handles
定義在OCL中所使用的變量
5 ReferenceHandle Borland.Eco.Handles 建立一個數據庫連接,在使用中相當於dbConnection 6 VariableHandle Borland.Eco.Handles 通常和OclVariables一起使用 7 PersistenceMapperBdp Borland.Eco.Persistence 將對象模型通過BdpConnection.進行發布 8 PersistenceMapperXML Borland.Eco.Persistence 將對象模型通過Xml格式.進行發布. 9 PersistenceMapperSqlServer Borland.Eco.Persistence 將對象模型通過SqlServer.進行發布 10 EcoAutoFormExtender Borland.Eco.WinForm 自動生成類信息窗體 11 EcoActionExtender Borland.Eco.WinForm 和Button的EcoAction屬性綁定,由按鈕事件實現對象方法 12 EcoDragDropExtender Borland.Eco.WinForm 提供Eco對象間在GUI中的拖曳支持 13 EcoListActionExtender Borland.Eco.WinForm 和Button的EcoListAction屬性綁定,用以控制CurrencyManagerHandle事件其中Extender類組件將標准的.Net組件(DataGrid,TextBox等)和ECO進行了關聯,這些Extender組件在由ECO Application Wizard創建完成後就自動生成了這些組件,我們不需要手工創建。我們下面用到的PersistenceMapper,是將對象模型發布到數據庫或XML中,同時也將對象實例存儲到數據庫中。如果我們在項目中需要轉換數據庫平台,不需要作過多的改變,僅僅將PersistenceMapper類型更改以下就可以,實現了真正的數據庫平台無關性。在本演示中我們用PersistenceMapperXML來進行數據的存儲,在程序完成需要發布時根據需要再改為其它類型的數據庫即可。
在工程視圖中雙擊打開EcoCRMEcoSpace.pas,並切換到設計視圖,從Tool Palette中雙擊PersistenceMapperXML,添加一個PersistenceMapperXML到設計器中,設置其FileName屬性為"EcoCrmData.xml",並將TEcoCRMEcoSpace的PersistenceMapper屬性設置為PersistenceMapperXML。
4. 編碼
下面我們要完成的就是界面設計、編碼工作。在工程視圖中雙擊WinForm.pas打開窗體編輯器。ECO Application Wizard已經創建了一個ReferenceHandle組件rhRoot和多個Extender組件。Extender組件保留其默認屬性即可,如果將其刪除用戶界面中的控件將無法和ECO關聯。設置rhRoot的EcoSpaceType 為 "EcoCRMEcoSpace.TEcoCRMEcoSpace",如果在EcoSpaceType屬性下拉列表中沒有選項,可重新編譯一下工程。
在界面上放置6個DataGrid,分別將其重命名為dgPersons、dgCompanys、dgCompanyCars、dgPersonCarSells、dgCompanyCars、dgPersonCars,為使界面更清晰可以分別設定一下DataGrid的CaptionText。並且在每個DataGrid上放置兩個按鈕,其Text分別為"添加"、"刪除"。完成界面設計為如下樣式。
用戶界面中的組件需要和ExpressionHandle相連接才能顯示感知對象實例。在這裡ExpressionHandle與Delphi中Dateset的概念很類似,它是一組數據的集合。向窗體編輯器中新添加一個ExpressionHandle,設置其Name屬性為ehCompanys,其RootHandle屬性為rhRoot,雙擊Expression打開OCL Expression編輯器。OCL Expression編輯器很類似於我們以前用的SQL編輯器,不過它使用的是OCL語法,在系統發布運行後ECO會根據不同的數據庫平台將OCL翻譯成相應的SQL進行執行。在OCL Expression編輯器中輸入"Company.allInstances",它的含義是取得Company的所有對象實例,我們可以將它等效理解為SQL語句中的" Select * from Company"。
同樣我們為其他的幾個類建立對應的ExpressionHandle,它們的名字分別為ehPersons、ehCompanyCarSells、ehPersonCarSells、ehCompanyCars、ehPersonCars。為實現主子表關聯關系我們還需要CurrencyManagerHandle組件,添加一個CurrencyManagerHandle組件到設計器中,將其重命名為cmhCompany,設定其RootHandle屬性為主對象ehCompany,其BindContext 屬性為顯示Company列表的dgCompanys。
CurrencyManagerHandle組件的Element屬性即表示對象集中的當前元素,它是根據BindContext屬性來進行判斷當前元素的。我們如果想要使ehCompanyCarSells與Company建立主子關聯關系則需要設置ehCompanyCarSells的RootHandle屬性為cmhCompany,Expression屬性為"self.CarSell"。建立其他需要進行主子表關聯的CurrencyManagerHandle,它們分別是cmhPerson、ehCompanyCarSells、ehPersonCarSells、ehCompanyCars、ehPersonCars。其屬性與cmhCompany類似。
主要組件屬性為:
ehPersons.Expression := 'Person.allInstances';
ehPersons.RootHandle := Self.rhRoot;
ehCompanyCars.Expression := 'self.Car';
ehCompanyCars.RootHandle := Self.cmhCompanyCarSell;
cmhCompanyCarSell.BindingContext := Self.dgCompanyCarSells;
cmhCompanyCarSell.RootHandle := Self.ehCompanyCarSells;
ehCompanyCarSells.Expression := 'self.CarSell';
ehCompanyCarSells.RootHandle := Self.cmhCompany;
cmhCompany.BindingContext := Self.dgCompanys;
cmhCompany.RootHandle := Self.ehCompanys;
ehCompanys.Expression := 'Company.allInstances';
ehCompanys.RootHandle := Self.rhRoot;
ehPersonCarSells.Expression := 'self.CarSell';
ehPersonCarSells.RootHandle := Self.cmhPerson;
cmhPerson.BindingContext := Self.dgPersons;
cmhPerson.RootHandle := Self.ehPersons;
ehPersonCars.Expression := 'self.Car';
ehPersonCars.RootHandle := Self.cmhPersonCarSell;
cmhPersonCarSell.BindingContext := Self.dgPersonCarSells;
cmhPersonCarSell.RootHandle := Self.ehPersonCarSells;
然後將剛才所設計的DataGrid和ExpressionHandle組件關聯,設置DataGrid的DateSource屬性為相對應的ExpressionHandle。下面我們需要為添加刪除方法編寫代碼,對於那些沒有主對象關聯關系的對象來說代碼十分簡單。
procedure TWinForm.btnPersonAdd_Click(sender: System.Object; e: System.EventArgs);
begin
Person.Create(EcoSpace);
end;
添加人員只用這一句代碼即可,刪除代碼同樣很簡單:
procedure TWinForm.btnPersonDelete_Click(sender: System.Object; e: System.EventArgs);
begin
Person(cmhPerson.Element.AsObject) .AsIObject.Delete
end;
添加刪除Company的代碼和這個類似,但帶有主對象關聯的對象在添加時候還需要為其主對象賦值。CompanyCarSells(企業購車紀錄)的添加代碼為:
CarSell.Create(EcoSpace).Customer := Customer(cmhCompany.Element.AsObject);
其中cmhCompany.Element.AsObject為當前所被選中的Company的值,將其轉換Customer類型賦值給新創建好的 CarSell.Create(EcoSpace).Customer。
我們需要完成的代碼十分簡單,為每個方法添加一句類似於上述的過程代碼即可。ECO帶給我們的變化的確令人驚喜。我們現在完成了對對象操作的代碼,下面還需要把所添加修改的事例保存到數據庫中,添加一個按鈕只需要執行一下EcoSpace.UpdateDatabase;就可以完成數據的保存工作。
系統運行界面如下:
在選擇客戶時,客戶汽車銷售紀錄會自動顯示相關紀錄,而浏覽銷售紀錄時下面會自動顯示與銷售紀錄相關的汽車信息。ECO還提供了AutoForm 功能,選擇一個DataGrid設置其EcoAutoForm屬性為True[圖16],在運行狀態時,雙擊DataGrid標題即可打開ECO自動生成的對象屬性Form。如果需要手工調用AutoForm可以用下面的代碼。
Uses
Borland.Eco.AutoContainers, Borland.Eco.ObjectRepresentation;
..
var
autoContainer: IAutoContainer;
begin
autoContainer := AutoContainerService.Instance.CreateContainer(
EcoSpace, cmhCompanyCarSell.Element);
Form(autoContainer).ShowDialog;
end;
後記
通過上面的實例我們已經看到MDA開發的威力了,它不僅大幅提高了開發效率,還以模型驅動的方式保證了設計和編碼的一致性。但是對於ECO來說,前面還有很長的路要走,Delphi 8的完成度遠遠達不到它以前的版本。在筆者寫DEMO過程中就發現了很多常規操作中的Bug,Borland即將推出Delphi 8 的Update,以修正其中的問題。現在Eco並不支持ASP.NET開發,如果不出意外在下個版本的Eco就會加入對ASP.NET的支持並添加更多易用的組件。Eco雖然還不夠成熟,但是我們還是能從上面看到很多軟件開發方式上的變化,相信隨著ECO的成熟和完善,人們也會更加廣泛的接納它。