⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tiofp documentation - a worked example of using the tiopf.htm

📁 tiOPF 面向对象的数据库持久层持久层开发的框架
💻 HTM
📖 第 1 页 / 共 5 页
字号:
test data by hard coding. The question is, where shall the instance be created? 
Who shall be responsible for creating it? And who shall be responsible for 
freeing it?</P>
<P>I usually choose from one of three strategies:</P>
<OL>
  <LI>Create is as an application wide, globally visible Singleton. This is a 
  useful technique when you are working with background data that can be shared 
  between objects, or when you are creating a single form application like the 
  one we are working on here. 
  <LI>Create it as an owned property of some sort of manager class. This 
  technique is good if there will be 0..n instances of the class, and you want 
  to create them on demand and share them between client classes once they have 
  been created. 
  <LI>Create it and free it in a form Create and free it in the constructor and 
  destructor of the class that it will interact with (often an application’s 
  form, or main form). This is good for MDI applications when you might want 
  several forms all editing the same class of object, but populated with 
  different data. </LI></OL>
<P>We shall create the TContactMgr class as a Singleton because we are working 
with a single form application and it makes it easier to abstract persistence 
code away from the GUI.</P>
<P>Create a unit called ContactMgr_Cli.pas, and add the following code:</P><PRE>unit ContactMgr_Cli;
interface
uses
  ContactMgr_BOM
  ;
// We hide a variable with unit wide scope behind a function.
function gContactMgr : TContactMgr ;
implementation
var
  uContactMgr : TContactMgr ;
  
// Fakes some of the behaviour expected of a Singleton
function gContactMgr : TContactMgr ;
begin
  // If uContactMgr has not been created, then create one.
  if uContactMgr = nil then
    uContactMgr := TContactMgr.Create ;
  result := uContactMgr ;
end ;

initialization
finalization
  uContactMgr.Free ;
end.</PRE>
<P>This fakes some of the behaviour expected from a Singleton. This technique 
will allow the singleton to be deleted, which is technically not permitted. It 
will also allow more than one instance of TContactMgr to be created which also 
violates the rules of a pure Singleton. The technique is, however, quick to code 
and easy to understand which is not the case for a pure GoF Singleton when 
implemented in Delphi.</P>
<P>Now, add the unit tiPtnVisPerObj_Cli to the uses clause of the Implementation 
section of the main unit then drop a button on the application’s main form. Add 
the following code to the button’s OnClick event handler:</P><PRE>procedure TFormMain.Button1Click(Sender: TObject);
begin
  tiShowPerObjAbs( gContactMgr ) ;
end;</PRE>
<P>This will output the contents of the empty to TContactMgr to a popup dialog 
like this:</P>
<P><IMG height=96 
src="tiOFP Documentation - A worked example of using the tiOPF_files/6_AWorkedExampleOfUsingTheTIOPF_clip_image001_0003.gif" 
width=412> </P>
<P>We will now populate TContactMgr with some hard coded data so create another 
unit called ContactMgr_TST.pas (TST stands for test) and add the following 
code:</P><PRE>unit ContactMgr_TST;
interface
uses
  ContactMgr_BOM
  ;
  
procedure PopulateContactMgr( pContactMgr : TContactMgr ) ;

implementation

procedure PopulateContactMgr( pContactMgr : TContactMgr ) ;
var
  lPerson : TPerson ;
begin
  lPerson := TPerson.Create ;
  lPerson.LastName := 'Hinrichsen' ;
  lPerson.FirstName := 'Peter' ;
  lPerson.Title := 'Mr' ;
  lPerson.Initials := 'P.W.' ;
  lPerson.Notes := 'Founder of the tiOPF project' ;
  pContactMgr.People.Add( lPerson ) ;
end ;</PRE>
<P>This will populate the contact manager with yours truly.</P>
<P>We have to add a call to PopulateContactMgr somewhere in application and as 
we are going down the singleton route for the creation of the TContactMgr, we 
can add the code there:</P><PRE>function gContactMgr : TContactMgr ;
begin
  // If uContactMgr has not been created, then create one.
  if uContactMgr = nil then
  begin
    uContactMgr := TContactMgr.Create ;
    PopulateContactMgr( uContactMgr ) ;
  end ;
  result := uContactMgr ;
end ;</PRE>
<P>This is what we are wanting so we can go on and create the remaining four 
classes.</P>
<H2>Coding TAdrsList, TEAdrsList, TAdrs and TEAdrs</H2>
<P>We shall not code the four classes TAdrsList, TEAdrsList, TAdrs and TEAdrs. 
There is a common property shared between TAdrs and TEAdrs called AdrsType, so 
we will create an abstract address class to provide this common behaviour, and 
descend both TAdrs and TEAdrs form this abstract class.</P>
<P>First, the interface of TPerson is extended with an owned instance of 
TAdrsList and TEAdrsList. These list classes are surfaced as published 
properties so they will be automatically detected by the iteration routine in 
the TVisited class. The interface extended interface of TPerson is shown 
below:</P><PRE>TPerson = class( TPerObjAbs )
private
  FAdrsList : TAdrsList ;
  FEAdrsList : TEAdrsList ;
protected
  function GetCaption : string ; override ;
  constructor Create ; override ;
  destructor Destroy ; override ;
published
  property AdrsList : TAdrsList read FAdrsList ;
end ;</PRE>
<P>The implementation of TPerson is as you would expect with an instance of 
TAdrsList and TEAdrsList being created and destroyed in the constructor and 
destructor. There are four extra lines of code however where the owner 
relationship between TAdrsList, TEAdrsList, their list elements and the TPerson 
are set. In the implementation of GetCaption, we will return the string 
FirstName + ‘ ’ + LastName.</P>
<P>Now, from deep in the hierarchy, at say the TAdrs level, we want to be able 
to chain up the object hierarchy like this to get the owning person like 
this:</P><PRE>lPerson := lAdrs.Owner.Owner ;</PRE>
<P>But with the TAdrs and TEAdrsList classes the way we would build them by 
default, the owner of a TAdrs is its TAdrsList. We really want the owner 
property of a TAdrs to reference the TPerson object like this:</P><PRE>lPerson := lAdrs.Owner ;</PRE>
<P>This is achieved by setting the ItemOwner property of the TAdrsList and 
TEAdrsList objects like this:</P><PRE>constructor TPerson.Create;
begin
  inherited;
  FAdrsList := TAdrsList.Create ;
  FAdrsList.Owner := self ;
  FAdrsList.ItemOwner := self ;
  FEAdrsList := TEAdrsList.Create ;
  FEAdrsList.Owner := Self ;
  FAdrsList.ItemOwner := Self ;
end;</PRE>
<P>This can be a little confusing. The owner of a TAdrs, from the point of view 
of the class being responsible for freeing the TAdrs is the TAdrsList. The owner 
of the TAdrs when TAdrs.Owner is called is the TPerson class, which is much more 
useful when traversing the object hierarchy. This is all taken care of in the 
TPerObjAbs.Add method.</P>
<P>Next, we create the interface and implementation of TAdrsListAbs, TAdrsList 
and TEAdrsList. These consist of new implementations of the methods GetItems, 
SetItems, GetOwner, SetOwner and Add with the compiler warnings being suppressed 
by using the reintroduce key word. The type of the property Items has also been 
changed. By changing the type of these properties, along with their get and set 
methods we firm up the relationship between the collection class and the objects 
that it holds. This takes a bit of extra work up front, but we will be more than 
compensated in saved development, debugging and maintenance time. The interface 
of TAdrsList is shown below:</P><PRE>TAdrsListAbs = class( TPerObjList )
protected
  function GetOwner: TPerson; reintroduce ;
  procedure SetOwner(const Value: TPerson); reintroduce ;
public
  property Owner : TPerson read GetOwner write SetOwner ;
end ;

TAdrsList = class( TAdrsListAbs )
private
protected
  function GetItems(i: integer): TAdrs ; reintroduce ;
  procedure SetItems(i: integer; const Value: TAdrs); reintroduce ;
public
  property Items[i:integer] : TAdrs read GetItems write SetItems ;
  procedure Add( pObject : TAdrs ; pDefDispOrdr : boolean = true ) ; reintroduce ;
published
end ;</PRE>
<P>The interface of TAdrsList is not shown, but as you would expect, it follows 
the same pattern as TPeople with each of the Get and Set methods simply calling 
inherited with some type casting as necessary. The interface and implementation 
of TEAdrsList follows the same pattern as in TAdrsList with the methods each 
calling inherited with the appropriate types casting.</P>
<P>TAdrs and TEAdrs both descend from the common parent TAdrsAbs because the 
have the property AdrsType in common. The interface of TAdrsAbs is shown 
below:</P><PRE>TAdrsAbs = class( TPerObjAbs )
private
  FAdrsType: string;
published
  property AdrsType : string read FAdrsType write FAdrsType ;
end ;</PRE>
<P>In TAdrs, the type of the Owner property is changed to TPerson along with the 
corresponding Get and Set methods. The properties Lines, Suburb, PCode, State, 
and country are also added. The interface of TAdrs is shown below:</P><PRE>TAdrs = class( TAdrsAbs )
private
  FCountry: string;
  FSuburb: string;
  FLines: string;
  FPCode: string;
  FState: string;
protected
  function GetCaption : string ; override ; 
  function GetOwner: TAdrsList; reintroduce ;
  procedure SetOwner(const Value: TAdrsList ); reintroduce ;
  property Owner : TAdrsList read GetOwner write SetOwner ;
published
  property Lines : string read FLines write FLines ;
  property Suburb : string read FSuburb write FSuburb ;
  property State : string read FState write FState ;
  property PCode : string read FPCode write FPCode ;
  property Country : string read FCountry write FCountry ;
end ;</PRE>
<P>You will now need to add the tiUtils unit to ContactMgr_BOM’s uses clause, as 
this is where the tiStrTran function can be found. The implementation of 
GetCaption creates a single line view of the address and is shown below:</P><PRE>function TAdrs.GetCaption: string;
begin
  result :=
  tiStrTran( tiStrTran( Lines, Cr, ' ' ), Lf, '' ) +
    ' ' + Suburb + ' ' + State + ' ' + PCode + ' ' +
    Country ;
end;</PRE>
<P>The interface of TEAdrs is the same as TAdrs except that the properties 
Lines, Suburb, etc are replaced with a single property called Text.</P>
<P>We can extend the PopulateContactMgr routine found in ContactMgr_TST.pas by 
creating instances of TAdrs and TEAdrs. This code looks like this:</P><PRE>procedure PopulateContactMgr( pContactMgr : TContactMgr ) ;
var
  lPerson : TPerson ;
  lAdrs : TAdrs ;
  lEAdrs : TEAdrs ;
begin
  lPerson := TPerson.Create ;
  lPerson.LastName := 'Hinrichsen' ;
  lPerson.FirstName := 'Peter' ;
  lPerson.Title := 'Mr' ;
  lPerson.Initials := 'P.W.' ;
  lPerson.Notes := 'Founder of the tiOPF project' ;
  pContactMgr.People.Add( lPerson ) ;
  lAdrs := TAdrs.Create ;
  lAdrs.AdrsType := 'Street' ;
  lAdrs.Lines := '23 Victoria Pde.' ;
  lAdrs.Suburb := 'Collingwood' ;
  lAdrs.PCode := '3066' ;
  lAdrs.State := 'VIC' ;
  lAdrs.Country := 'Australia' ;
  lPerson.AdrsList.Add( lAdrs ) ;
  lAdrs := TAdrs.Create ;
  lAdrs.AdrsType := 'Postal' ;
  lAdrs.Lines := 'PO Box 429' ;
  lAdrs.Suburb := 'Abbotsford' ;
  lAdrs.PCode := '3067' ;
  lAdrs.State := 'VIC' ;
  lAdrs.Country := 'Australia' ;
  lPerson.AdrsList.Add( lAdrs ) ;
  lEAdrs := TEAdrs.Create ;
  lEAdrs.AdrsType := 'EMail' ;
  lEAdrs.Text := 'peter_hinrichsen@techinsite.com.au' ;
  lPerson.EAdrsList.Add( lEAdrs ) ;
  lEAdrs := TEAdrs.Create ;
  lEAdrs.AdrsType := 'Mobile' ;
  lEAdrs.Text := '0418 108 353' ;
  lPerson.EAdrsList.Add( lEAdrs ) ;
  lEAdrs := TEAdrs.Create ;
  lEAdrs.AdrsType := 'Fax' ;
  lPerson.EAdrsList.Add( lEAdrs ) ;
  lEAdrs := TEAdrs.Create ;
  lEAdrs.Text := '+61 3 9419 1682' ;
  lPerson.EAdrsList.Add( lEAdrs ) ;
end ;</PRE>
<P>When we run the application and click the button with the tiShowPerObjAbs( ) 
call, as expected we get the following dialog:</P>
<P><IMG height=399 
src="tiOFP Documentation - A worked example of using the tiOPF_files/6_AWorkedExampleOfUsingTheTIOPF_clip_image002.jpg" 
width=300> </P>
<P>This completes our work with the business object model, so next we can set up 
the GUI to display the data using a combination of the TtiTreeView, TtiListView 
and TtiPerAware controls.</P>
<H2>Setup the GUI to display the object hierarchy</H2>
<H2>Set-up the main form</H2>
<P>Add a TToolBar, TActionList, TMainMenu, TStatusBar and TImageList to the 
form. Name the TToolBar TB, the TMainMenu MM, the TStatusBar SB, and the 
TImageList to ILButtons (we will eventually have 2 image lists on this form). 
Set the TToolBar’s Flat property to true, and change its height to 25. Add four 
buttons and two separators to the toolbar, positioning the separators before the 
3rd and 4th buttons. Now, add a Close menu item to the File menu, and New, 
Delete and Save menu items to the Edit option of the main menu you added. We 
will hook up the appropriate actions in a moment.
<P>
<P>Double click the image list and add the New, Delete, Save and Cancel images 
from the \TechInsite\Images\Buttons directory. Arrange the images in order as 
shown below:</P>
<P><IMG height=218 
src="tiOFP Documentation - A worked example of using the tiOPF_files/6_AWorkedExampleOfUsingTheTIOPF_clip_image002_0000.jpg" 
width=331> </P>
<P>Double click the Action list and add four actions: aNew, aDelete, aSave and 
aClose. Setup their captions to read &amp;New, &amp;Delete, &amp;Save and 
&amp;Close, with their shortcuts being Ins, Del, Ctrl+S and Alt+F4. Note, that 
we can’t actually assign Alt+F4 as a shortcut key from the Object Inspector; we 
must do this by writing the following piece of code in the main form’s 
FormCreate event handler:</P><PRE>Procedure TFormMain.FormCreate(Sender : TObject);
Begin
  aClose.ShortCut := ShortCut(VK_F4, [ssAlt]);
End;</PRE>
<P>The Shortcut function is defined in the Menus unit, so don’t forget to add 
this to the uses clause, or your code will not compile.</P>
<P>Assign the image index properties of each action to match its image index in 
the image list. Set the Hint properties to ‘New’, ‘Delete’, ‘Save’ and ‘Close’. 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -