Ваш браузер устарел. Рекомендуем обновить его до последней версии.

Extra-speed app development based on OOP & VCL

I want to talk about my technology for quick and easy application development. I have been using this technology for more than 10 years. At the heart of this idea is the OOP and the set of visual components that I created. Perhaps this can be a long time to explain what and how it works, but I think it will be easier to show by example.

 

Let's say that it is required to create a user application that has the ability to work with a list of people. The data of each person will contain general information: name, date of birth, contact information, address and some biometric data. The application should provide visualization of data and perform such actions as adding, editing, deleting and storing data of the list of people.

 

From the point of view of OOP, all the necessary data can be represented as objects. As a result, we will create several classes such as Address, Date, Contact, Biometrics. Each of these classes has properties and at the same time each class is a property of the main Person class. All Person objects are organized as a list. 

 

      •  A long time ago I developed a generic class inherited from TObject that can contain a list of objects inside it and has the ability to save and load itself, its properties and objects of its list. Variants of object storage can be a stream, a binary file or a text file. As format of the text representation the Delphi form .dfm- like format was used. This generic class is the core of my technology.

So, how much time would it take to create such a workable application? Not more than a couple of hours. The first part is not visual - create the necessary classes and determine their properties. Also at this stage, custom descriptions are created to adjust the visualization of some class properties. And the second part is creating a visual form and to place there a couple of visual components and write a few lines of code.

      • The fact is that I added the possibility of adjusting the visualization and editing of the object and its properties to the nonvisualpart of the class description. This can be done by creating special auxiliary objects or by adding attributes of the class's properties using RTTI. By defining and describing a class once, it becomes possible to visualize and represent it's data without additional settings at each place where the object representation is used. This is true if you use visual components that can use the description of the visualization. In addition, when the class is inherited, its properties also inherit the tweaks of the representation.

Well, look at the developed application. It is a form containing visual components for representing data in the grid and the property editor of the selected object

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

Download demo application

 

Source code of form

unit frmTestForm;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, psClasses, ioClasses, ioControls, ioTree, StdCtrls, ioGrid, 
  ExtCtrls, ImgList;

type
  TFormDemoIO = class(TForm)
    IOGrid1: TIOGrid;
    IOTree1: TIOTree;
    ImageList1: TImageList;
    procedure FormCreate(Sender: TObject);
    procedure IOGrid1ClickTitleControl(Sender: TIOControlItem);
    procedure IOGrid1GetStateImage(AObject: TObject; var Bitmap: TBitmap);
    procedure FormDestroy(Sender: TObject);
  private
    BMP: TBitmap;
  public
end;

var
  FormDemoIO: TFormDemoIO;

implementation

{$R *.dfm}

// data file name
function DataFileName: String;
begin
  Result := ExtractFilePath(ParamStr(0)) + '\persons.txt';
end;

procedure TFormDemoIO.FormCreate(Sender: TObject);
begin
  BMP := TBitmap.Create;
  psPersons := TPSPersons.Create;

  if FileExists(DataFileName) then
    psPersons.LoadFromTextFile(DataFileName);

  ioGrid1.List := psPersons.Items;
  ioGrid1.ListObjectClass := TPSPerson;
end;

procedure TFormDemoIO.FormDestroy(Sender: TObject);
begin
  BMP.Free;
  psPersons.Free;
end;

// Save button click
procedure TFormDemoIO.IOGrid1ClickTitleControl(Sender: TIOControlItem);
begin
if Sender.Name = 'btn_Save'then
    psPersons.SaveToTextFile(DataFileName);
end;

// draw image on status column
procedure TFormDemoIO.IOGrid1GetStateImage(AObject: TObject; var Bitmap: TBitmap);
begin
  BMP.Canvas.FillRect(Rect(0, 0, 15, 15));
  if Assigned(AObject) then
    ImageList1.GetBitmap(Ord(TPSPerson(AObject).Biometric.Sex), BMP);
  Bitmap := BMP;
end;

end.

Source code of used classes

unit psClasses;

interface

uses
  Classes, SysUtils, ioClasses, ioClassDef,
  Graphics;

type
  //////////////////////////////////////////////////////////////////////////////
  ///  object to keep address
  TPSAddress = class(TIOBaseObject)
  {$REGION}
  private
    FCountry: String;
    FCity: String;
    FAddr: String;
    FZIP: String;
  protected
  public
  published{$ENDREGION}
  property ZIP: Stringread FZIP write FZIP;
    property Addr: Stringread FAddr write FAddr;
    property City: Stringread FCity write FCity;
    property Country: Stringread FCountry write FCountry;
  end;

  //////////////////////////////////////////////////////////////////////////////
  ///  object to keep contact information
  TPSContact = class(TIOBaseObject)
  {$REGION}
  private
    FEMail: String;
    FPhone: String;
    FWebsite: String;
  protected
  public
  published{$ENDREGION}
    [TIOFieldAttr(2, 'phone no')]  // assign visual props via RTTI
    property Phone: Stringread FPhone write FPhone;
    [TIOFieldAttr(3, 'web site')]
    property Website: Stringread FWebsite write FWebsite;
    [TIOFieldAttr(1, 'e-mail')]
    property EMail: Stringread FEMail write FEMail;
  end;

  //////////////////////////////////////////////////////////////////////////////
  ///  enumerate monthes
  TPSMonth = (
    psJan, psFeb, psMar, psApr, psMay, psJun,
    psJul, psAug, psSep, psOkt, psNov, psDec);

  //////////////////////////////////////////////////////////////////////////////
  ///  date
  TPSDate = class(TIOBaseObject)
  {$REGION}
    private
    FDate: TDateTime;
    function GetDay: Integer;
    function GetMonth: TPSMonth;
    function GetYear: Word;
    procedure SetDay(const Value: Integer);
    procedure SetMonth(const Value: TPSMonth);
    procedure SetYear(const Value: Word);
    function GetDisplayText: String;
  protected
  public
  published{$ENDREGION}
    property Date: TDateTime read FDate write FDate;
    property Year: Word read GetYear write SetYear;
    property Day: Integer read GetDay write SetDay;
    property Month: TPSMonth read GetMonth write SetMonth;
    property DisplayText: Stringread GetDisplayText;
  end;

  //////////////////////////////////////////////////////////////////////////////
  TPSSex = (
    sxNone,
   sxMale,
    sxFemale);

  //////////////////////////////////////////////////////////////////////////////
  TPSBiometric = class(TIOBaseObject)
  {$REGION}
  private
    FAllowed: Boolean;
    FWeight: Double;
    FSex: TPSSex;
    FHeight: Double;
    FEyes: TColor;
  protected
  public
  published{$ENDREGION}
    [TIOFieldAttr(1, 'allow')]
    property Allowed: Boolean read FAllowed write FAllowed;
    [TIOFieldAttr(2, 'gender')]
    property Sex: TPSSex read FSex write FSex;
    [TIOFieldAttr(3, 'height', 'F=#0.0|M=cm')]
    property Height: Double read FHeight write FHeight;
    [TIOFieldAttr(4, 'weight', 'F=#0.0|M=kg')]
    property Weight: Double read FWeight write FWeight;
    [TIOFieldAttr(5, 'eyes color')]
    property Eyes: TColor read FEyes write FEyes;
  end;

  //////////////////////////////////////////////////////////////////////////////
  TPSPerson = class(TIOBaseObject)
  {$REGION}
  private
    FLastName: String;
    FFirstName: String;
    FDOB: TPSDate;
    FAddress: TPSAddress;
    FContact: TPSContact;
    FBiometric: TPSBiometric;
    procedure SetAddress(const Value: TPSAddress);
    procedure SetContact(const Value: TPSContact);
    procedure SetDOB(const Value: TPSDate);
    procedure SetBiometric(const Value: TPSBiometric);
  protected
  public
    constructor Create; override;
    destructor Destroy; override;

  published{$ENDREGION}
    property FirstName: Stringread FFirstName write FFirstName;
    property LastName: Stringread FLastName write FLastName;
    property DOB: TPSDate read FDOB write SetDOB;
    property Contact: TPSContact read FContact write SetContact;
    property Address: TPSAddress read FAddress write SetAddress;
    property Biometric: TPSBiometric read FBiometric write SetBiometric;
  end;

  //////////////////////////////////////////////////////////////////////////////
  ///  person list
  TPSPersons = class(TIOBaseObject)
  {$REGION}
  private
    function GetPerson(Index: Integer): TPSPerson;
  protected
  public{$ENDREGION}
    property Person[Index: Integer]: TPSPerson read GetPerson; default;
 end;

implementation

// code folded as uninteresting
[TPSTate]
{$REGION 'TPSPerson'} constructor TPSPerson.Create; begin Inherited; FDOB := TPSDate.Create; FAddress := TPSAddress.Create; FContact := TPSContact.Create; FBiometric := TPSBiometric.Create; end; destructor TPSPerson.Destroy; begin FContact.Free; FAddress.Free; FDOB.Free; FBiometric.Free; inherited; end; procedure TPSPerson.SetAddress(const Value: TPSAddress); begin FAddress.Assign(Value); end; procedure TPSPerson.SetBiometric(const Value: TPSBiometric); begin FBiometric.Assign(Value); end; procedure TPSPerson.SetContact(const Value: TPSContact); begin FContact.Assign(Value); end; procedure TPSPerson.SetDOB(const Value: TPSDate); begin FDOB.Assign(Value); end; {$ENDREGION} {$REGION 'TPSPersons'} function TPSPersons.GetPerson(Index: Integer): TPSPerson; begin Result := TPSPerson(Items[Index]); end; {$ENDREGION} initialization RegisterClasses([TPSAddress, TPSContact, TPSBiometric, TPSDate, TPSPerson]); ////////////////////////////////////////////////////////////////////////////// /// represent JAN instead of psJAN in editors with ClassDefs.CreateClassDef('TPSMonth', '') do SetSubstList([ 'psJan=JAN', 'psFeb=FEB', 'psMar=MAR', 'psApr=APR', 'psMay=MAY', 'psJun=JUN', 'psJul=JUL', 'psAug=AUG', 'psSep=SEP', 'psOkt=OKT', 'psNov=NOV', 'psDec=DEC']); ////////////////////////////////////////////////////////////////////////////// /// show Data and create node 'details' for Day, Months and Year with ClassDefs.CreateClassDef(TPSDate, '') do begin AddFieldEx('Date', '', '', '', 'dd-mmm-yyyy'); AddFieldEx('Year', 'Year', '', 'details', '0000'); AddFieldEx('Day', 'Day', '', 'details', '00').Measure := '##'; AddFieldEx('Month', 'Month', '', 'details'); AddFieldEx('DisplayText', '', '', '').StyleGrid.Hide := True; end; ////////////////////////////////////////////////////////////////////////////// with ClassDefs.CreateClassDef(TPSPerson, '') do begin with AddFieldEx('LastName', 'Last name', '') do begin StyleData.DrawStyle.FontStyle := [fsBold]; StyleGrid.MinWidth := 80; SortingOrder := 1; end; with AddFieldEx('FirstName', 'First name', '') do begin StyleData.DrawStyle.FontStyle := [fsBold]; StyleGrid.MinWidth := 80; end; AddFieldEx('DOB', 'birth date', '').StyleGrid.MinWidth := 60; AddFieldEx('Contact', 'Contact', ''); AddFieldEx('Address', 'Address', ''); AddFieldEx('Biometric', 'Biometric data', ''); end; ////////////////////////////////////////////////////////////////////////////// { default visual settings used for this object with ClassDefs.CreateClassDef(TPSAddress) do begin AddFieldEx('ZIP', '', ''); AddFieldEx('Addr', 'Address', ''); AddFieldEx('City', '', ''); AddFieldEx('Country', '', ''); end;} end.