home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue59 / Arch / Extended Sample / UnitObjectBase.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  2000-05-29  |  7.9 KB  |  259 lines

  1. unit UnitObjectBase;
  2.  
  3. interface
  4.  
  5. uses
  6.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
  7.  
  8. type
  9.   TObjectBase = class;
  10.   TObjectBaseClass = class of TObjectBase;
  11.   TObjectBase = class(TDataModule)
  12.   private
  13.     FReferencingObjectList: TList;
  14.     FKey: integer;
  15.     class function FindReference(anObjectClass: TObjectBaseClass; const aKey: integer): TObjectBase;
  16.     procedure AddReference(aReferencingObject: TComponent);
  17.     constructor PrivateCreate(Owner: TComponent);
  18.     procedure AncestorInitializeObject;
  19.     procedure AbstractFinalizeObject;
  20.   protected
  21.  
  22.     procedure InitializeObject; virtual;
  23.     procedure FinalizeObject; virtual;
  24.  
  25.   public
  26.  
  27.     constructor Create(Owner: TComponent); override;
  28.     destructor Destroy; override;
  29.  
  30.     // Saving, canceling, and updates pending are concepts that probably
  31.     // only relate to entity objects. However, I'm introducing the methods
  32.     // at the top of the hierarchy for convenience.
  33.     class function AnyUpdatesPending: boolean;
  34.     function UpdatesPending: boolean; virtual;
  35.     procedure Save; virtual;
  36.     procedure Cancel; virtual;
  37.     class procedure SaveAll;
  38.  
  39.     function Equals(aClass: TObjectBaseClass; aKey: integer): boolean; overload; virtual;
  40.     function Equals(anObject: TObjectBase): boolean; overload; virtual;
  41.  
  42.     // FetchReference and FreeReference are the only ways to create or
  43.     // destroy TObjectBase objects.
  44.     class function FetchReference(aReferencingObject: TComponent; aKey: integer): TObjectBase;
  45.     procedure FreeReference(aReferencingObject: TComponent);
  46.  
  47.     property Key: integer read FKey;
  48.  
  49.     class function NewInteger: integer;
  50.  
  51.   end;
  52.  
  53. var
  54.   ObjectBase: TObjectBase;
  55.  
  56. implementation
  57.  
  58. {$R *.DFM}
  59.  
  60. uses UnitFormBase, UnitObjectEntityBase;
  61.  
  62. var PrivateClassObjectList: TList;
  63.  
  64. function TObjectBase.Equals(aClass: TObjectBaseClass; aKey: integer): boolean;
  65. begin
  66.   // Return TRUE if the object type and key match.
  67.   Result := (Self <> NIL) and (Self.ClassType = aClass) and (Self.Key = aKey);
  68. end;
  69.  
  70. function TObjectBase.Equals(anObject: TObjectBase): boolean;
  71. begin
  72.   // Objects are equal if they are both NIL, or if they are both of
  73.   // the same class and have the same key.
  74.   if ( (Self = NIL) and (anObject = NIL) ) then
  75.     Result := TRUE
  76.   else if ( (Self <> NIL) or (anObject <> NIL) ) then
  77.     Result := FALSE
  78.   else
  79.     Result := (Self.ClassType = anObject.ClassType) and (Self.Key = anObject.Key);
  80. end;
  81.  
  82. procedure TObjectBase.AddReference(aReferencingObject: TComponent);
  83. var i: integer;
  84. begin
  85.   // Increment the reference count for the referencing object. If the
  86.   // referencing object is already on the list then don't add it a
  87.   // second time.
  88.   i := FReferencingObjectList.IndexOf(aReferencingObject);
  89.   if (i = -1) then
  90.     FReferencingObjectList.Add(aReferencingObject);
  91. end;
  92.  
  93. constructor TObjectBase.Create(Owner: TComponent);
  94. begin
  95.   // Programmers aren't allowed to run this method -- they have to use
  96.   // FetchReference. If this gets run odds are the TDataModule wasn't
  97.   // removed from the auto-create list.
  98.   inherited;
  99.   MessageDlg('TObjectBase.Create is being run. Check that the data module ' + Self.Name + ' is not being auto-created.', mtError, [mbOK], 0);
  100. end;
  101.  
  102. class function TObjectBase.FetchReference(aReferencingObject: TComponent; aKey: integer): TObjectBase;
  103. begin
  104.   // "Self" refers to the class type in class methods.
  105.  
  106.   // See if the object already exists.
  107.   Result := FindReference(Self, aKey);
  108.  
  109.   if (Result = NIL) then begin
  110.     // It doesn't exist...
  111.  
  112.     // Owner = NIL, because it's up to the referencing objects to free up
  113.     // those references. If Owner were set to Application then upon
  114.     // Applicatoin.Terminate the Application might delete objects before
  115.     // we were done with them.
  116.     Result := Self.PrivateCreate(NIL);
  117.     // Add the new object to the list of existing objects.
  118.     PrivateClassObjectList.Add(Result);
  119.     // Initialize the Key property.
  120.     Result.FKey := aKey;
  121.     Result.AncestorInitializeObject;
  122.     // Add the referencing object to the new object's list of references.
  123.     Result.AddReference(aReferencingObject);
  124.     Result.InitializeObject;
  125.   end
  126.   else begin
  127.     // Add the referencing object to the new object's list of references.
  128.     Result.AddReference(aReferencingObject);
  129.   end; // else begin
  130. end;
  131.  
  132. class function TObjectBase.FindReference(anObjectClass: TObjectBaseClass; const aKey: integer): TObjectBase;
  133. var i: integer;
  134. var anObject: TObjectBase;
  135. begin
  136.   // Return the object using class type and key. If it doesn't exist
  137.   // return NIL.
  138.   Result := NIL;
  139.   for i := 0 to PrivateClassObjectList.Count - 1 do begin
  140.     anObject := TObjectBase(PrivateClassObjectList.Items[i]);
  141.     if (anObject.Equals(anObjectClass, aKey)) then begin
  142.       Result := anObject;
  143.       break;
  144.     end; // then begin
  145.   end; // do begin
  146. end;
  147.  
  148. procedure TObjectBase.FreeReference(aReferencingObject: TComponent);
  149. var i: integer;
  150. begin
  151.   // Exit if the the programmer runs the routine after the reference has 
  152.   // already been cleared.
  153.   if (Self = NIL) then exit;
  154.  
  155.   // Remove the referencing object from the list of referencing objects.
  156.   // If the reference count goes to zero then destroy the object.
  157.   i := FReferencingObjectList.IndexOf(aReferencingObject);
  158.   if (i = -1) then begin
  159.     // If the referencing object isn't found at all then something is  wrong.
  160.     // It's probably most appropriate to raise an excaption here, but for
  161.     // debugging purposes it may be best to just display an error message.
  162.     MessageDlg('TObjectBase.FreeReference being run by a referencing object not found on the reference list', mtError, [mbOK], 0);
  163.     exit;
  164.   end; // then begin
  165.   FReferencingObjectList.Delete(i);
  166.   if (FReferencingObjectList.Count = 0) then begin
  167.     // There are no references to this object. Free it up.
  168.     FinalizeObject;
  169.     PrivateClassObjectList.Remove(Self);
  170.     AbstractFinalizeObject;
  171.     // Run Destroy in the ancestor because we aren't allowing the programmer
  172.     // to run TObjectBase.Destroy
  173.     inherited Destroy;
  174.   end; // then begin
  175. end;
  176.  
  177. destructor TObjectBase.Destroy;
  178. begin
  179.   // This should never be run. Raise an exception so the tester knows what's
  180.   // going on.
  181.   MessageDlg('Error: TObjectBase.Destroy being run. Use FreeReference instead', mtWarning, [mbOK], 0);
  182.   inherited;
  183. end;
  184.  
  185. procedure TObjectBase.AbstractFinalizeObject;
  186. begin
  187.   FReferencingObjectList.Free;
  188. end;
  189.  
  190. procedure TObjectBase.AncestorInitializeObject;
  191. begin
  192.     FReferencingObjectList := TList.Create;
  193. end;
  194.  
  195. constructor TObjectBase.PrivateCreate(Owner: TComponent);
  196. begin
  197.   inherited Create(Owner);
  198. end;
  199.  
  200. procedure TObjectBase.FinalizeObject;
  201. begin
  202.   //
  203. end;
  204.  
  205. procedure TObjectBase.InitializeObject;
  206. begin
  207.   //
  208. end;
  209.  
  210. function TObjectBase.UpdatesPending: boolean;
  211. begin
  212.   Result := FALSE;
  213. end;
  214.  
  215. class procedure TObjectBase.SaveAll;
  216. var i: integer;
  217. var anObject: TObjectBase;
  218. begin
  219.   // Run Save on each entity object
  220.     for i := 0 to (PrivateClassObjectList.Count - 1) do begin
  221.     anObject := (TObjectBase(PrivateClassObjectList.Items[i]));
  222.     if (anObject is TObjectEntityBase) then
  223.       TObjectEntityBase(anObject).Save;
  224.   end; // do begin
  225. end;
  226.  
  227. class function TObjectBase.AnyUpdatesPending: boolean;
  228. var i: integer;
  229. begin
  230.   Result := FALSE;
  231.     for i := 0 to (PrivateClassObjectList.Count - 1) do
  232.       Result := Result or TObjectBase(PrivateClassObjectList.Items[i]).UpdatesPending;
  233. end;
  234.  
  235. class function TObjectBase.NewInteger: integer;
  236. begin
  237.   Result := TFormBase.NewInteger;
  238. end;
  239.  
  240. procedure TObjectBase.Cancel;
  241. begin
  242.   //
  243. end;
  244.  
  245. procedure TObjectBase.Save;
  246. begin
  247.   //
  248. end;
  249.  
  250. initialization
  251.   PrivateClassObjectList := TList.Create;
  252.  
  253. finalization
  254.   if (PrivateClassObjectList.Count <> 0) then
  255.     MessageDlg('Appliation terminating with objects remaining in memory.', mtWarning, [mbOK], 0);
  256.   PrivateClassObjectList.Free;
  257.  
  258. end.
  259.