DELPHI 的 3rd-Party 元件數量之多,遠超過其它的開發工具,其用途之廣可說創下前所未有的記錄。這也為 DELPHI 程式 設計師省下許多重新製造輪子的時間,令系統開發速度倍增,同時減少了因實作碼增加而使錯誤率升高。可惜的是 VCL 元件似乎都有 著一個通病,就是缺少完整的說明檔 ! 許多 VCL 元件甚至連範例都少的可憐,幸運的是 VCL 元件有個不 成文的慣例,那就是多數都會附上完整的原始碼,這一點可以稍減其說明檔不足的現象。即便如此,說明檔不足依舊對使用者造成相當大的困擾,時間就是金錢,在 設計者探索原始碼時,時間也一點點的流失了。本文所介紹的 RemObjects SDK( 以下簡稱 RO) 也不能例 外,由於這套元件的開發者只有兩位,因此說明檔一直都是相當短缺,有些地方甚至還有描述錯誤的情形,但這些缺點卻無法掩蓋其嶄新的創意與高延展性的設計概 念,這也是本文為何會出現在讀者眼前的主要原因, RO 是筆者看過 VCL 元件中唯一令筆者感到驚豔的,當然 ! 這只是筆者個人 的感覺,對讀者不見得是如此,不過多了解一樣東西,於汝何損 ?? 因此,細細品嘗吧 !!
參考文章
|
電子商務新紀
元
|


元件
|
功能
|
類別
|
TROBinMessagw
,
TROSOAPMessage
|
訊息元件,用來處理訊息。
|
訊息類
|
TROIndyHTTPServer
,
TROIndyTCPServer
,
TROBPDXHTTPServer
,
TROBPDXTCPServer
,
TROWinMessageServer
|
Server
端元件,用來接收訊息,支援
HTTP
、
TCP
、
Window Message
與
DLL(DLL
不需要元件,只需
export
一個
function
即可
)
|
Server
類
|
TROIndyHTTPChannel
,
TROIndyTCPChannel
,
TROBPDXHTTPChannel
,
TROBPDXTCPChannel
,
TROWinInetHTTPChannel
,
TRODLLChannel
|
Client
端元件,用來送出訊息到
Server
端,支援
HTTP
,
TCP
,
Windows Message
與
DLL
。
|
Client
類
|
TRODataSnapConnection
,
TRODataSnapProviderPublisher
|
支援
DataSnap
運 作的元件,是的,
RemObjects SDK
允許使用
DataSnap
運行於其上。
|
DataSnap
支援
|
TROWebBrokerServer
|
Web Broker
支援,允許任何架構於
Web Broker
之上的網頁程式直接掛載
RemObjects SDK Server
。
|
Web
支援
|

Wizard
|
說明
|
Apache 2 Shared Module Server Project
|
建立
Apache 2 Shared Module
的
Server
程式。
|
Apache Shared Module Server Project
|
建立
Apache 1.x Shared Module
的
Server
程式。
|
DLL Server Project
|
建立
DLL
的
Server
程 式。
|
ISAPI/NSAPI Server Project
|
建立
ISAPI
、
NSAPI
的
Server
程 式。
|
RemObjects DataSnap Server Module
|
建立一個支援
DataSnap
操 作的
TRODataSnapDataModule
。
|
Windows Executable Server Project
|
建立一個可獨立執行的
Server
。
|

欄位
|
說明
|
Project Name
|
專案名稱。
|
Service Library Name
|
Library
名稱,在
RO 1.x
中這個參數的用途不大。
|
Service Name
|
Web Service
的名稱。
|
Server Class
|
通訊協定,見
Server
類元 件。
|
Message Class
|
訊息協定,見
Client
類元 件。
|
Project Directory
|
專案存放的目錄。
|


function TCalcService.Sum(const A: Integer; const B: Integer): Integer;
begin
Result:=A+B;
end;
function TCalcService.GetServerTime: DateTime;
begin
Result:=Now;
end;
|


//CalcService_EndPointURI = 'http://localhost/soap';
CalcService_EndPointURI = 'http://localhost:8099/soap';
//CalcService_DefaultURN = 'urn-NewLibrary:CalcService';
CalcService_DefaultURN = 'CalcService';
|

……………
uses CalcService_Intf;
procedure TfmMain.btnCalcClick(Sender: TObject);
var
vService:CalcService;
begin
vService:= CoCalcService.Create(ROSOAPMessage1,ROWinInetHTTPChannel1);
try
lblResult.Caption:=IntToStr(vService.Sum(StrToInt(edtValue1.Text),StrToInt(edtValue2.Text)));
finally
vService:=Nil;
end;
end;
procedure TfmMain.Button1Click(Sender: TObject);
var
vService:CalcService;
begin
vService:= CoCalcService.Create(ROSOAPMessage1,ROWinInetHTTPChannel1);
try
lblServerTime.Caption:=DateTimeToStr(vService.GetServerTime);
finally
vService:=Nil;
end;
end;
|

RODL2 /rodl:CalcLibrary.rodl /language:pascal /type:async
|
uses CalcLibrary_Async;
procedure TfmMain.btnCalcClick(Sender: TObject);
var
vService:CalcService_Async;
begin
ROWinInetHTTPChannel1.TargetURL:='http://localhost:8099/soap';
vService:=CoCalcService_Async.Create(ROSOAPMessage1,ROWinInetHTTPChannel1);
try
vService.Invoke_Sum(StrToInt(edtValue1.Text),StrToInt(edtValue2.Text));
Sleep(3000);
lblResult.Caption:=IntToStr(vService.Retrieve_Sum);
finally
vService:=Nil;
end;
end;
|


function TfmMain.GetService:CalcService;
begin
Result:=Nil;
case RadioGroup1.ItemIndex of
0:
begin
ROWinInetHTTPChannel1.TargetURL:='http://localhost:8099/soap';
Result:=CoCalcService.Create(ROSOAPMessage1,ROWinInetHTTPChannel1);
end;
1:
begin
ROWinInetHTTPChannel1.TargetURL:='http://localhost:8099/bin';
Result:=CoCalcService.Create(ROBINMessage1,ROWinInetHTTPChannel1);
end;
end;
end;
procedure TfmMain.btnCalcClick(Sender: TObject);
var
vService:CalcService;
begin
vService:=GetService;
try
lblResult.Caption:=IntToStr(vService.Sum(StrToInt(edtValue1.Text),StrToInt(edtValue2.Text)));
finally
vService:=Nil;
end;
end;
procedure TfmMain.Button1Click(Sender: TObject);
var
vService:CalcService;
begin
vService:=GetService;
try
lblServerTime.Caption:=DateTimeToStr(vService.GetServerTime);
finally
vService:=Nil;
end;
end;
|




procedure TfmMain.btnCalcClick(Sender: TObject);
var
vService:CalcService;
begin
vService:=CoCalcService.Create(ROBINMessage1,ROIndyTCPChannel1);
try
lblResult.Caption:=IntToStr(vService.Sum(StrToInt(edtValue1.Text),StrToInt(edtValue2.Text)));
finally
vService:=Nil;
end;
end;
procedure TfmMain.Button1Click(Sender: TObject);
var
vService:CalcService;
begin
vService:=CoCalcService.Create(ROBINMessage1,ROIndyTCPChannel1);
try
lblServerTime.Caption:=DateTimeToStr(vService.GetServerTime);
finally
vService:=Nil;
end;
end;
|
procedure TfmMain.btnCalcClick(Sender: TObject);
var
vService:CalcService;
begin
vService:=CoCalcService.Create(ROBINMessage1,ROWinMessageChannel1);
try
lblResult.Caption:=IntToStr(vService.Sum(StrToInt(edtValue1.Text),StrToInt(edtValue2.Text)));
finally
vService:=Nil;
end;
end;
procedure TfmMain.Button1Click(Sender: TObject);
var
vService:CalcService;
begin
vService:=CoCalcService.Create(ROBINMessage1,ROWinMessageChannel1);
try
lblServerTime.Caption:=DateTimeToStr(vService.GetServerTime);
finally
vService:=Nil;
end;
end;
|


function TComplexTypeService.GetPerson: Person;
begin
Result:=Person.Create;
Result.Name:='
黃忠成
';
Result.Age:=18;
end;
|
Procedure TfmMain.Button1Click(Sender: TObject);
var
vService:ComplexTypeService;
vData:Person;
begin
vService:=CoComplexTypeService.Create(ROSOAPMessage1,ROWinInetHTTPChannel1);
try
vData:=vService.GetPerson;
try
edtName.Text:=vData.Name;
edtAge.Text:=IntToStr(vData.Age);
finally
vData.Free;
end;
finally
vService:=Nil;
end;
end;
|




Initialization
{ To adjust scalability of the DataSnapModule and avoid recreation of
instances for each roundtrip, you might want to choose a different
ClassFactory type.
Make sure to check the following url for more information:
http://www.remobjects.com/page.asp?id=C771266D-6D99-4301-B77D-D7B92D3BCD4D }
//TROPooledClassFactory.Create('IAppServer', Create_DataSnapModule, TIAppServer_Invoker, 25);
//TROClassFactory.Create('IAppServer', Create_DataSnapModule, TIAppServer_Invoker);
TROPerClientClassFactory.Create('IAppServer', Create_DataSnapModule, TIAppServer_Invoker, 20*60 { seconds Session timeout }) ;
|






unit CalcLibrary_Invk;
{----------------------------------------------------------------------------}
{ This unit was automatically generated by the RemObjects SDK after reading }
{ the RODL file associated with this project . }
{ }
{ Do not modify this unit manually, or your changes will be lost when this }
{ unit will regenerated the next time you compile the project. }
{----------------------------------------------------------------------------}
interface
uses
{vcl:} Classes,
{RemObjects:} uROServer, uROServerIntf, uROTypes, uROClientIntf,
{Generated:} CalcLibrary_Intf;
type
TCalcService_Invoker = class(TROInvoker)
private
protected
published
procedure Invoke_Sum(const __Instance:IInterface; const __Message:IROMessage; const __Transport:IROTransport);
procedure Invoke_GetServerTime(const __Instance:IInterface; const __Message:IROMessage; const __Transport:IROTransport);
end;
implementation
uses
{RemObjects:} uRORes;
{ TCalcService_Invoker }
procedure TCalcService_Invoker.Invoke_Sum(const __Instance:IInterface; const __Message:IROMessage; const __Transport:IROTransport);
{ function Sum(const A: Integer; const B: Integer): Integer; }
var
A: Integer;
B: Integer;
Result: Integer;
begin
try
__Message.Read('A', TypeInfo(Integer), A, []);
__Message.Read('B', TypeInfo(Integer), B, []);
Result := (__Instance as CalcService).Sum(A, B);
__Message.Initialize(__Transport, 'CalcLibrary', 'CalcService', 'SumResponse');
__Message.Write('Result', TypeInfo(Integer), Result, []);
__Message.Finalize;
finally
end;
end;
procedure TCalcService_Invoker.Invoke_GetServerTime(const __Instance:IInterface; const __Message:IROMessage; const __Transport:IROTransport);
{ function GetServerTime: DateTime; }
var
Result: DateTime;
begin
try
Result := (__Instance as CalcService).GetServerTime;
__Message.Initialize(__Transport, 'CalcLibrary', 'CalcService', 'GetServerTimeResponse');
__Message.Write('Result', TypeInfo(DateTime), Result, [paIsDateTime]);
__Message.Finalize;
finally
end;
end;
end.
|
unit CalcLibrary_Intf;
{----------------------------------------------------------------------------}
{ This unit was automatically generated by the RemObjects SDK after reading }
{ the RODL file associated with this project . }
{ }
{ Do not modify this unit manually, or your changes will be lost when this }
{ unit will regenerated the next time you compile the project. }
{----------------------------------------------------------------------------}
interface
uses
{vcl:} Classes, TypInfo,
{RemObjects:} uROProxy, uROTypes, uROClientIntf;
const
LibraryUID = '{D62FF60B-5567-43E3-8386-161869372E27}';
type
{ Forward declarations }
CalcService = interface;
{ CalcService }
{ Description:
Service CalcService. This service has been automatically generated using the RODL template you can find in the Templates directory. }
CalcService = interface
['{D62FF60B-5567-43E3-8386-161869372E27}']
function Sum(const A: Integer; const B: Integer): Integer;
function GetServerTime: DateTime;
end;
{ CoCalcService }
CoCalcService = class
class function Create(const aMessage : IROMessage; aTransportChannel : IROTransportChannel) : CalcService;
end;
implementation
uses
{vcl:} SysUtils,
{RemObjects:} uRORes;
type
TCalcService_Proxy = class(TROProxy, CalcService)
private
protected
function Sum(const A: Integer; const B: Integer): Integer;
function GetServerTime: DateTime;
end;
{ CoCalcService }
class function CoCalcService.Create(const aMessage : IROMessage; aTransportChannel : IROTransportChannel) : CalcService;
begin
result := TCalcService_Proxy.Create(aMessage, aTransportChannel);
end;
{ TCalcService_Proxy }
function TCalcService_Proxy.Sum(const A: Integer; const B: Integer): Integer;
var __request, __response : TMemoryStream;
begin
__request := TMemoryStream.Create;
__response := TMemoryStream.Create;
try
__Message.Initialize(__TransportChannel, 'CalcLibrary', 'CalcService', 'Sum');
__Message.Write('A', TypeInfo(Integer), A, []);
__Message.Write('B', TypeInfo(Integer), B, []);
__Message.Finalize;
__Message.WriteToStream(__request);
__TransportChannel.Dispatch(__request, __response);
__Message.ReadFromStream(__response);
__Message.Read('Result', TypeInfo(Integer), result, []);
finally
__request.Free;
__response.Free;
end
end;
function TCalcService_Proxy.GetServerTime: DateTime;
var __request, __response : TMemoryStream;
begin
__request := TMemoryStream.Create;
__response := TMemoryStream.Create;
try
__Message.Initialize(__TransportChannel, 'CalcLibrary', 'CalcService', 'GetServerTime');
__Message.Finalize;
__Message.WriteToStream(__request);
__TransportChannel.Dispatch(__request, __response);
__Message.ReadFromStream(__response);
__Message.Read('Result', TypeInfo(DateTime), result, [paIsDateTime]);
finally
__request.Free;
__response.Free;
end
end;
initialization
end.
|
unit CalcService_Impl;
{----------------------------------------------------------------------------}
{ This unit was automatically generated by the RemObjects SDK after reading }
{ the RODL file associated with this project . }
{ }
{ This is where you are supposed to code the implementation of your objects. }
{----------------------------------------------------------------------------}
interface
uses
{vcl:} Classes,SysUtils,
{RemObjects:} uROClientIntf, uROTypes, uROServer, uROServerIntf,
uRORemoteDataModule,
{Generated:} CalcLibrary_Intf;
type
TCalcService = class(TRORemoteDataModule, CalcService)
private
protected
function Sum(const A: Integer; const B: Integer): Integer;
function GetServerTime: DateTime;
end;
implementation
{$R *.DFM}
uses
{Generated:} CalcLibrary_Invk;
procedure Create_CalcService(out anInstance : IUnknown);
begin
anInstance := TCalcService.Create(NIL);
end;
function TCalcService.Sum(const A: Integer; const B: Integer): Integer;
begin
Result:=A+B;
end;
function TCalcService.GetServerTime: DateTime;
begin
Result:=Now;
end;
initialization
TROClassFactory.Create('CalcService', Create_CalcService, TCalcService_Invoker);
finalization
end.
|
ClassFactory
|
說明
|
建立方式
|
TROClassFactory
|
預設產生的
ClassFactory
, 會在收到
Client
端要求後建立目標物件,完成呼叫動作後釋放該物件,簡單的說就是
Single-Call
模 式。
|
TROClassFactory.Create('CalcService', Create_CalcService, TCalcService_Invoker);
|
TROSingletonClassFactory
|
Singleton
模式,不管
Client
端有多少個,只使用一個物件來服務所有
Client
端,使用這 種模式必須注意多執行緒下的關鍵區段控制。
|
TROSingletonClassFactory.Create('SingletonService', Create_SingletonService, TSingletonService_Invoker);
|
TROSynchronizedSingletonClassFactory
|
與
TROSingletonClassFactroy
相同,唯一不同之處是此
ClassFactory
會自行處理多執行緒下的關鍵區段控制,不需設計者擔心。
|
TROSynchronizedSingletonClassFactory.Create('SingletonService', Create_SingletonService, TSingletonService_Invoker);
|
TROPooledClassFactory
|
使用
Pooling
機制來 建立物件,簡單的說是預先建立一定數量的物件,不管
Client
端有多少,
Server
端就只有一定數量的物件服務所有
Client
端,假如
Client
端大於
Server
端物件數 量,有些
Client
端會面臨排隊等待或是失敗
(
取決於第五個參數
)
。
Server
端的物件不會自行釋放。
|
TROPooledClassFactory.Create('PooledService', Create_PooledService, TPooledService_Invoker, 2,[pbCreateAdditional],false);
最後三個參數分別代表著
Pool
的數量,也就是 最大物件數,
pbCreateAdditional
則是告知當
Client
端大於
Pool
物件數量時,是否建立新物件來因應,這個參數可以是
pbWait
,要
Client
端等待, 或是
pbFail
,直接傳回錯誤訊息,
pbCreateAdditional
則是直接建立新的物件服務
Client
端,這個新物件將以
Single-Call
模 式執行,第三個參數則是控制是否在程式啟動後就將
Pool
數量的物件建立好,如果這個參數是否的話,那麼物件的建立會被延後到第一個
Client
端連上後 再執行。
|
TROPerClientClassFactory
|
使用
Session
機制來建 立物件,簡單的說就是每一個
Client
端都會有專用的
Server
端物件,也又是具狀態的模式。
|
TROPerClientClassFactory.Create('SessionSample', Create_SessionSample, TSessionSample_Invoker, 20*60 { seconds Session timeout });
最後一個參數是設定這個物件在建立後經過多少時間沒動 作之後就釋放掉。
|