The purpose of this tutorial is to present how-to use the With Class CASE tool to develop Delphi programs. With Class is a scripting CASE tool which permits the creation of object-oriented diagrams, text specifications, and code generation. In this tutorial we separate the user interface classes from application (problem domain) classes. To create Delphi applications, we will follow these steps:
1 - In Delphi, create a Delphi Project and create the user interface consisting of forms, menus, dialog boxes, etc.
2 - Identify the application/problem domain entities (classes) that hold application information and process application information with algorithms.
3 - Create a class diagram of application (problem domain) classes in With Class.
4 - Generate Delphi code from the class diagram using a script, e.g. DELBASIC.SCT.
5 - In Delphi, add the generated code .pas files into the Delphi project.
6 - Compile and execute the project.
7 - Attach the user interface form (Form1) with the application classes, e.g. create application objects and create messages to the application objects.
8 - After modifying the Delphi code, reverse engineer
the code in With Class to create a reversed class diagram.
Using With Class to Create a Class Diagram and
Generate Delphi Code
To create your first Delphi program in With Class,
launch With Class and set the Utilities - Preferences to Delphi.
Create the class diagram, counter.omt. Select the class icon or
Draw - Class. Place the class on the diagram. Enter the class
name Counter. Enter the attribute Value. Enter the operation names
Increment and Decrement. To display the maximum information on
the class diagram, select View - Show Class - then select Show
Type, Show Visibility, Show Operation Parameters, and Show Attributes/Operation
Properties. On the class diagram, the - indicates private visibility,
the + indicates public visibility, and the # indicates protected
visibility. To generate Delphi code select Utilities - Generate
Code per File. Select Delphi. If necessary, select Browse to find
the script file DELBASIC.SCT. To view the generated code counter.pas,
select File - Edit File and open counter.pas. Then open Delphi,
create a project, add counter.pas. Compile and execute the project.
Once you have created the class diagram, double click
on the class to bring up the Class Form shown below.
In the Class Form, click on the Spec Button to update
the class specification shown below.
On the Class Form, double click on the attribute
to bring up the Attribute Specification. Enter the Attribute Type,
e.g. Integer, Attribute Visibility, e.g. private, and the Attribute
Initial Value, e.g. 0. As appropriate, check a box for Constant,
Static (Class), Array, Read Property, or Write Property. The script
DELALL.SCT generates read and write property code statements.
On the Class Form, double click on the Increment
operation to bring up the Operation Specification shown below.
Enter the Operation Return Type for the procedure and the Operation
Visibility, e.g. public. For each parameter enter the parameter
type/class name followed by the parameter name similar to C++
form, e.g. Integer anIntegerParameter. With Class will generate
the Delphi parameters, e.g. anIntegerParameter : Integer.
Click on the Code button to bring up the Code Form
shown below. Enter Delphi code for the Increment Operation, e.g.
Value = Value + 1; Later, if you update this code in the Delphi
Editor, you can import the updated code by selecting the Import
Delphi button.
Once the class diagram and all specifications have
been completed, you may generate Delphi code by selecting Utilities
- Generate Code File per Class shown below. Select Delphi and
the script DELBASIC.SCT.
The DELBASIC.SCT script is listed below. This is the most basic Delphi code generation script. This script reads the class diagram and creates the Counter.pas Delphi code file. Key points about this script:
- T is prefixed to each class name, e.g. Counter on the diagram becomes TCounter class in the generated code.
- All attributes (Delphi fields) and aggregation role names (Delphi fields) are private.
- All association role names (Delphi fields) and operations (Delphi methods) are public
- 1 to 1 Association is implemented by creating a Delphi field but no object is created. The field may be set to nil in the constructor. The caller must new and free the 1 to 1 Association object.
- 1 to 1 Aggregation is implemented by creating a Delphi field and creating an object. This means new should be used in constructors and free should be used in destructors for 1:1 aggregation part objects.
- 1 to Many Association and 1 to Many Aggregation relationships are implemented as arrays. You must update the array index with the correct multiplicity, e.g.[1..4];
- Create constructor and Destroy destructor are created.
- Uses statement is generated including unit names
for Inheritance - Ancestor Classes, 1 to 1 Association, 1 to 1
Aggregation, 1 to Many Association, and 1 to Many Aggregation,
e.g. uses SysUtils, Messages, Classes ;
Script to Generate Delphi Code - DELBASIC.SCT
- Very Basic Code - Starting Point
CLASS_USER1
unit CLASS_NAME;
interface
CLASS_USER2
CLASS_USER3
uses SysUtils, Messages, Classes [,DELETE_LAST_SYMBOL
BASE_CLASS ][,DELETE_LAST_SYMBOL ASSOCIATION_ONE_CLASS ][,DELETE_LAST_SYMBOL
AGGREGATION_ONE_CLASS ][,DELETE_LAST_SYMBOL ASSOCIATION_MANY_CLASS
][,DELETE_LAST_SYMBOL AGGREGATION_MANY_CLASS ][,DELETE_LAST_SYMBOL
INCLUDE_FILE ] ;
type
T$CLASS_NAME = class [(T$BASE_CLASS)]
private
[COLUMN_TAB(4) ATTRIBUTE_NAME : ATTRIBUTE_TYPE ; ]
[COLUMN_TAB(4) AGGREGATION_ONE_NAME : T$AGGREGATION_ONE_CLASS ; ]
[COLUMN_TAB(4) AGGREGATION_MANY_NAME : array LITERAL_SYMBOL[1..10 LITERAL_SYMBOL] of T$AGGREGATION_MANY_CLASS ; ]
public
[COLUMN_TAB(4) ASSOCIATION_ONE_NAME : T$ASSOCIATION_ONE_CLASS ; ]
[COLUMN_TAB(4) ASSOCIATION_MANY_NAME : array LITERAL_SYMBOL[1..10 LITERAL_SYMBOL] of T$ASSOCIATION_MANY_CLASS ; ]
constructor Create; virtual; {replace
virtual with override in derived classes}
destructor Destroy ; override;
SELECT_WHEN OPERATION_IS_PROCEDURE [
procedure OPERATION_NAME PASCAL_OPERATION_PARAMETERS_WITH_VAR
; PASCAL_OPERATION_VIRTUAL PASCAL_OPERATION_ABSTRACT PASCAL_OPERATION_OVERRIDE
]
SELECT_WHEN OPERATION_IS_FUNCTION [
function OPERATION_NAME PASCAL_OPERATION_PARAMETERS PASCAL_OPERATION_RETURN_TYPE ; PASCAL_OPERATION_VIRTUAL PASCAL_OPERATION_ABSTRACT PASCAL_OPERATION_OVERRIDE]
end;
implementation
{-------------------------------------------------------------------------------}
constructor T$CLASS_NAME$.Create;
begin
inherited Create ;
{ Place code here }
end;
{-------------------------------------------------------------------------------}
{Update as required.}
destructor T$CLASS_NAME$.Destroy ;
begin
{ Place code here }
inherited Destroy;
end;
SELECT_WHEN OPERATION_IS_PROCEDURE
[{------------------------------------------------------------------------------}
procedure T$CLASS_NAME.OPERATION_NAME PASCAL_OPERATION_PARAMETERS_WITH_VAR ;
begin
{ Place code here }
OPERATION_CODE
end;
]
SELECT_WHEN OPERATION_IS_FUNCTION
[{------------------------------------------------------------------------------}
function T$CLASS_NAME.OPERATION_NAME PASCAL_OPERATION_PARAMETERS PASCAL_OPERATION_RETURN_TYPE ;
begin
{ Place code here and return an object }
OPERATION_CODE
end;
]
end.
Generated Delphi Code for the Counter Unit - counter.pas
- Very Basic
unit Counter;
interface
uses SysUtils, Messages, Classes ;
type
TCounter = class
private
Value : Integer ;
public
constructor Create; virtual; {replace virtual with override
in derived classes}
destructor Destroy ; override;
procedure Increment ;
procedure Decrement ;
end;
implementation
{-------------------------------------------------------------------------------}
constructor TCounter.Create;
begin
inherited Create ;
{ Place code here }
end;
{-------------------------------------------------------------------------------}
destructor TCounter.Destroy ; {Update as required.}
begin
{ Place code here }
inherited Destroy;
end;
{-------------------------------------------------------------------------------}
procedure TCounter.Increment ;
begin
{ Place code here }
end;
{-------------------------------------------------------------------------------}
procedure TCounter.Decrement ;
begin
{ Place code here }
end;
end.
Reversed Class Diagram from the Generated Delphi
Code - counter.pas
Using With Class to Generate Delphi Code for Major
O-O Relationships
To show you a more complex example, the following
is the class diagram for a Car Simulation. This diagram shows
all major object-oriented relationships, e.g. inheritance "type
of", 1 to 1 association "knows about", 1 to 1 aggregation
"part of", 1 to many association "knows about",
and 1 to many aggregation "part of". This class diagram
shows the Unified Modeling Language class diagram symbols: arrow
- inheritance, line - association, diamond - aggregation, * -
1 to many, - private, + public, # protected, and $ static.
The diagram below shows a Car class with inheritance,
association and aggregation relationships to other classes. The
purpose of this example is to show a diagram with inheritance
Vehicle to Car, 1:1 association Car to CellularPhone, 1:1 aggregation
Car to Motor, 1:Many association Car to Passenger, and 1:Many
aggregation Car to Tire.
Key points and suggested guidelines in creating the class diagram and generating Delphi code are:
- For each attribute (Delphi field), select Write_Property to generate a Write Property.
- For each attribute (Delphi field), select Read_Property to generate a Read Property.
- Capitalize all class, attribute and operation names.
- For each operation check the Procedure Checkbox for procedures. Do not check the Procedure Checkbox for functions.
- For each operation enter the operation return type and operation parameters. For each parameter enter the parameter type/class name followed by the parameter name similar to C++ form, e.g. Integer anIntegerParameter. With Class will generate the Delphi parameters, e.g. anIntegerParameter : Integer.
- For each 1:1 association, 1:1 aggregation, 1:Many
association, and 1:Many aggregation, enter a relationship role
name on the relationship line when you connect two classes, e.g.
CurrentCellularPhone. A field is created in the TCar class, e.g.
CurrentCellularPhone : CellularPhone.
The DELALL.SCT script is listed below. This is an
extensive Delphi code generation script. This script reads the
class diagram and creates the Vehicle.pas, Car.pas, Motor.pas,
CellularPhone.pas, Passenger.pas, and Tire.pas Delphi code files.
Key points about this script:
- T is prefixed to each class name, e.g. Car on the
diagram becomes TCar class in the generated code.
- F is prefixes to each attribute name, e.g. Speed
on the diagram becomes FSpeed in the generated code as a private
Delphi field name. Protected Get function and Set procedure are
generated.
- If the Read or Write property box is checked then
a property is created, e.g. Speed on the diagram causes a property
to be created, e.g. property Speed : Integer read GetSpeed;
- Visibility (Private/Public/Protected/Published/Automated)
is based upon the specified visibility of attributes (Delphi fields)
and operations (Delphi methods).
- 1 to 1 Association "knows about" is implemented
by creating a Delphi field but no object is created. This means
that the caller must new and free the 1:1 association object.
For example, the 1:1 association between Car and CellularPhone
is implemented with the Delphi field in the TCar class - currentCellularPhone
: TCellularPhone. For each 1:1 association object, a Get function
and Set and Remove procedures are created.
- 1 to 1 Aggregation "part of" is implemented
by creating a Delphi field and creating an object. This means
the assembly object does a new and free on the aggregation part
object. For example, the 1:1 aggregation between Car and Motor
is implemented with the Delphi field in the TCar class - currentMotor
: TMotor. Since aggregation implies completely hidden part objects,
no Get functions or Set procedures are created.
- 1 to Many Association relationship "knows
about" is implemented as a TList collection. For example,
the 1:1 association between Car and Passenger is implemented with
the Delphi field in the TCar class - currentPassengers : TList.
For each 1:M association object, a Public Add procedure is created
- AddPassenger (aPassenger : TPassenger). For each 1:M association
object, a Public Remove procedure is created - RemovePassenger
(aPassenger : TPassenger).
- 1 to Many Aggregation relationship "part of"
is implemented as a TList collection. For example, the 1:1 aggregation
between Car and Tire is implemented with the Delphi field in the
TCar class - currentTires : TList. Since aggregation implies completely
hidden part objects, no Add and Remove procedures are created.
The assembly object does a new and free on the aggregation part
object.
- In the Create constructor attributes (Delphi fields)
are initialized. 1 to 1 association objects are set to nil. List
objects and aggregation part objects are created with new.
- A Destroy destructor is created. List objects and
aggregation part objects are destroyed with free. Nothing is done
with association objects since they must be freed in the caller.
- Uses statement is generated including unit names
for Inheritance - Ancestor Classes, 1 to 1 Association, 1 to 1
Aggregation, 1 to Many Association, and 1 to Many Aggregation,
e.g. uses SysUtils, Messages, Classes , Vehicle , CellularPhone,
Motor, Passenger, Tire;
Script to Generate Delphi Code - DELALL.SCT
CLASS_USER1
unit CLASS_NAME;
interface
CLASS_USER2
CLASS_USER3
uses SysUtils, Messages, Classes [,DELETE_LAST_SYMBOL
BASE_CLASS ] [,DELETE_LAST_SYMBOL ASSOCIATION_ONE_CLASS ][,DELETE_LAST_SYMBOL
AGGREGATION_ONE_CLASS ][,DELETE_LAST_SYMBOL ASSOCIATION_MANY_CLASS
][,DELETE_LAST_SYMBOL AGGREGATION_MANY_CLASS ][,DELETE_LAST_SYMBOL
INCLUDE_FILE ] ;
type
T$CLASS_NAME = class [(T$BASE_CLASS)]
private
SELECT_WHEN ATTRIBUTE_ACCESS == private [COLUMN_TAB(4) F$ATTRIBUTE_NAME : ATTRIBUTE_TYPE ; ]
[COLUMN_TAB(4) ASSOCIATION_ONE_NAME : T$ASSOCIATION_ONE_CLASS ; {1:1 Association Field }]
[COLUMN_TAB(4) AGGREGATION_ONE_NAME : T$AGGREGATION_ONE_CLASS ; {1:1 Aggregation Part Field}]
[COLUMN_TAB(4) ASSOCIATION_MANY_NAME : TList ; {1:Many Association Field} ]
[COLUMN_TAB(4) AGGREGATION_MANY_NAME
: TList ; {1:Many Aggregation Part Field} ]
SELECT_WHEN OPERATION_ACCESS == private LOGICAL_AND OPERATION_IS_PROCEDURE [
procedure OPERATION_NAME PASCAL_OPERATION_PARAMETERS_WITH_VAR
; PASCAL_OPERATION_VIRTUAL PASCAL_OPERATION_ABSTRACT PASCAL_OPERATION_OVERRIDE
PASCAL_OPERATION_MESSAGE]
SELECT_WHEN OPERATION_ACCESS == private LOGICAL_AND OPERATION_IS_FUNCTION [
function OPERATION_NAME PASCAL_OPERATION_PARAMETERS
PASCAL_OPERATION_RETURN_TYPE ; PASCAL_OPERATION_VIRTUAL PASCAL_OPERATION_ABSTRACT
PASCAL_OPERATION_OVERRIDE]
protected
SELECT_WHEN ATTRIBUTE_ACCESS == protected
[COLUMN_TAB(4) F$ATTRIBUTE_NAME : ATTRIBUTE_TYPE
; ]
SELECT_WHEN OPERATION_ACCESS == protected LOGICAL_AND OPERATION_IS_PROCEDURE [
procedure OPERATION_NAME PASCAL_OPERATION_PARAMETERS_WITH_VAR
; PASCAL_OPERATION_VIRTUAL PASCAL_OPERATION_ABSTRACT PASCAL_OPERATION_OVERRIDE
PASCAL_OPERATION_MESSAGE]
SELECT_WHEN OPERATION_ACCESS == protected LOGICAL_AND OPERATION_IS_FUNCTION [
function OPERATION_NAME PASCAL_OPERATION_PARAMETERS
PASCAL_OPERATION_RETURN_TYPE ; PASCAL_OPERATION_VIRTUAL PASCAL_OPERATION_ABSTRACT
PASCAL_OPERATION_OVERRIDE]
[ function Get$ATTRIBUTE_NAME : ATTRIBUTE_TYPE;
{Get Accessor Function }]
[ procedure Set$ATTRIBUTE_NAME ( a$ATTRIBUTE_NAME
: ATTRIBUTE_TYPE ); {Set Accessor Procedure }]
published
SELECT_WHEN ATTRIBUTE_ACCESS == published
[COLUMN_TAB(4) F$ATTRIBUTE_NAME : ATTRIBUTE_TYPE ; ]
SELECT_WHEN OPERATION_ACCESS == published LOGICAL_AND OPERATION_IS_PROCEDURE [
procedure OPERATION_NAME PASCAL_OPERATION_PARAMETERS_WITH_VAR
; PASCAL_OPERATION_VIRTUAL PASCAL_OPERATION_ABSTRACT PASCAL_OPERATION_OVERRIDE
PASCAL_OPERATION_MESSAGE]
SELECT_WHEN OPERATION_ACCESS == published LOGICAL_AND OPERATION_IS_FUNCTION [
function OPERATION_NAME PASCAL_OPERATION_PARAMETERS
PASCAL_OPERATION_RETURN_TYPE ; PASCAL_OPERATION_VIRTUAL PASCAL_OPERATION_ABSTRACT
PASCAL_OPERATION_OVERRIDE]
public
SELECT_WHEN ATTRIBUTE_ACCESS == public
[COLUMN_TAB(4) F$ATTRIBUTE_NAME : ATTRIBUTE_TYPE ; ]
SELECT_WHEN OPERATION_ACCESS == public LOGICAL_AND OPERATION_IS_PROCEDURE [
procedure OPERATION_NAME PASCAL_OPERATION_PARAMETERS_WITH_VAR
; PASCAL_OPERATION_VIRTUAL PASCAL_OPERATION_ABSTRACT PASCAL_OPERATION_OVERRIDE
PASCAL_OPERATION_MESSAGE]
SELECT_WHEN OPERATION_ACCESS == public LOGICAL_AND OPERATION_IS_FUNCTION [
function OPERATION_NAME PASCAL_OPERATION_PARAMETERS PASCAL_OPERATION_RETURN_TYPE ; PASCAL_OPERATION_VIRTUAL PASCAL_OPERATION_ABSTRACT PASCAL_OPERATION_OVERRIDE]
constructor Create; virtual; { override;
replace virtual with override in derived classes }
{Update to initialize ancestor fields.}
constructor Initialize ( [a$ATTRIBUTE_NAME
: ATTRIBUTE_TYPE DELETE_LAST_SYMBOL;] NO_RETURN);
destructor Destroy ; override;
function EqualTo ( a$CLASS_NAME : T$CLASS_NAME
) : Boolean ;
SELECT_WHEN ATTRIBUTE_IS_READ_PROPERTY LOGICAL_AND ATTRIBUTE_IS_WRITE_PROPERTY [
property ATTRIBUTE_NAME : ATTRIBUTE_TYPE
read Get$ATTRIBUTE_NAME write Set$ATTRIBUTE_NAME ;]
SELECT_WHEN ATTRIBUTE_IS_READ_PROPERTY LOGICAL_AND LOGICAL_NOT ATTRIBUTE_IS_WRITE_PROPERTY [
property ATTRIBUTE_NAME : ATTRIBUTE_TYPE
read Get$ATTRIBUTE_NAME;]
SELECT_WHEN ATTRIBUTE_IS_WRITE_PROPERTY LOGICAL_AND LOGICAL_NOT ATTRIBUTE_IS_READ_PROPERTY [
property ATTRIBUTE_NAME : ATTRIBUTE_TYPE
write Set$ATTRIBUTE_NAME ;]
[ procedure Set$ASSOCIATION_ONE_CLASS
( a$ASSOCIATION_ONE_CLASS : T$ASSOCIATION_ONE_CLASS ) ; {Set Accessor
Procedure for 1:1 Association}]
[ function Get$ASSOCIATION_ONE_CLASS
: T$ASSOCIATION_ONE_CLASS ; {Get Assessor Function for 1:1 Association}
]
[ procedure Remove$ASSOCIATION_ONE_CLASS
;]
[ procedure Set$ASSOCIATION_MANY_CLASS$List
( a$ASSOCIATION_MANY_CLASS$List : TList ) ; {Set Accessor Procedure
for 1:Many Association}]
[ function Get$ASSOCIATION_MANY_CLASS$List
: TList ;{Get Assessor Function for 1:Many Association} ]
[ procedure Add$ASSOCIATION_MANY_CLASS
( a$ASSOCIATION_MANY_CLASS : T$ASSOCIATION_MANY_CLASS ) ;]
[ function Get$ASSOCIATION_MANY_CLASS
( Index : Integer ) : T$ASSOCIATION_MANY_CLASS ;{Get Assessor
Function for 1:Many Association}]
[ function Get$ASSOCIATION_MANY_CLASS$Count
: Integer ;]
[ procedure Remove$ASSOCIATION_MANY_CLASS
( a$ASSOCIATION_MANY_CLASS : T$ASSOCIATION_MANY_CLASS ) ;]
[ procedure RemoveAll$ASSOCIATION_MANY_CLASS;]
end;
implementation
{-----------------------------------------------------------------------------------------------}
constructor T$CLASS_NAME$.Create; {Association Objects Must be Created by Caller}
[NO_REPEAT var
AGGREGATION_MANY_NAME$Index : Integer;]
begin
inherited Create ;
[ F$ATTRIBUTE_NAME := ATTRIBUTE_INITIAL_VALUE; ]
[ ASSOCIATION_ONE_NAME := nil; {Caller must create ASSOCIATION_ONE_CLASS } ]
[ AGGREGATION_ONE_NAME := T$AGGREGATION_ONE_CLASS$.Create;{Create 1:1 Aggregation Part Object}]
[ ASSOCIATION_MANY_NAME := TList$.Create;{Create List Object for 1:1 Association Objects} ]
[ AGGREGATION_MANY_NAME := TList$.Create; {Create List Object for 1:1 Aggregation Part Objects}
for AGGREGATION_MANY_NAME$Index := 1 to 2 do {Update 2 with the number of AGGREGATION_MANY_CLASS$'s }
AGGREGATION_MANY_NAME.Add (T$AGGREGATION_MANY_CLASS$.Create);{Create AGGREGATION_MANY_CLASS objects}]
end;
{-----------------------------------------------------------------------------------------------}
{Update to initialize ancestor fields.}
constructor T$CLASS_NAME$.Initialize ( [a$ATTRIBUTE_NAME : ATTRIBUTE_TYPE DELETE_LAST_SYMBOL;] NO_RETURN);
[NO_REPEAT var
AGGREGATION_MANY_NAME$Index : Integer;]
begin
inherited Create; {Update for inherited Initialize with Arguments}
[ F$ATTRIBUTE_NAME := a$ATTRIBUTE_NAME;]
[ ASSOCIATION_ONE_NAME := nil; {Caller must create ASSOCIATION_ONE_CLASS } ]
[ AGGREGATION_ONE_NAME := T$AGGREGATION_ONE_CLASS$.Create;{Create 1:1 Aggregation Part Object}]
[ ASSOCIATION_MANY_NAME := TList$.Create;{Create List Object for 1:1 Association Objects} ]
[ AGGREGATION_MANY_NAME := TList$.Create; {Create List Object for 1:1 Aggregation Part Objects}
for AGGREGATION_MANY_NAME$Index := 1 to 2 do {Update 2 with the number of AGGREGATION_MANY_CLASS$'s }
AGGREGATION_MANY_NAME.Add (T$AGGREGATION_MANY_CLASS$.Create);{Create AGGREGATION_MANY_CLASS objects}]
end;
{-----------------------------------------------------------------------------------------------}
destructor T$CLASS_NAME$.Destroy ;
[NO_REPEAT var
AGGREGATION_MANY_NAME$Index : Integer;
Temp$AGGREGATION_MANY_NAME : T$AGGREGATION_MANY_CLASS ; ]
begin
[ AGGREGATION_ONE_NAME$.Free; {Frees 1:1 Aggregation Part Object} ]
[ ASSOCIATION_MANY_NAME$.Free; {Frees 1:Many Association List-caller must free Association objects} ]
[ for AGGREGATION_MANY_NAME$Index := 1 to AGGREGATION_MANY_NAME$.Count do
begin
Temp$AGGREGATION_MANY_NAME := AGGREGATION_MANY_NAME$.Items LITERAL_SYMBOL[ AGGREGATION_MANY_NAME$Index LITERAL_SYMBOL] ;
Temp$AGGREGATION_MANY_NAME.Free; {Frees 1:Many Aggregation Part Objects}
end;
AGGREGATION_MANY_NAME$.Free;{Frees 1:Many Aggregation List Object} ]
inherited Destroy;
end;
{-----------------------------------------------------------------------------------------------}
function T$CLASS_NAME.EqualTo ( a$CLASS_NAME : T$CLASS_NAME ) : Boolean;
begin { Does a field by field comparison - update to compare ancestor fields }
EqualTo := True;
[if (F$ATTRIBUTE_NAME <> a$CLASS_NAME.F$ATTRIBUTE_NAME) then EqualTo := False ;]
end;
SELECT_WHEN OPERATION_IS_PROCEDURE
[{-----------------------------------------------------------------------------------------------}
procedure T$CLASS_NAME.OPERATION_NAME PASCAL_OPERATION_PARAMETERS_WITH_VAR ;
begin
{ Place code here }
OPERATION_CODE
end;
]
SELECT_WHEN OPERATION_IS_FUNCTION
[{-----------------------------------------------------------------------------------------------}
function T$CLASS_NAME.OPERATION_NAME PASCAL_OPERATION_PARAMETERS PASCAL_OPERATION_RETURN_TYPE ;
begin
{ Place code here and return an object }
OPERATION_CODE
end;
]
[{-----------------------------------------------------------------------------------------------}
function T$CLASS_NAME.Get$ATTRIBUTE_NAME : ATTRIBUTE_TYPE; {Accessor Get Function }
begin
Get$ATTRIBUTE_NAME := F$ATTRIBUTE_NAME;
end;
]
[{-----------------------------------------------------------------------------------------------}
procedure T$CLASS_NAME.Set$ATTRIBUTE_NAME ( a$ATTRIBUTE_NAME : ATTRIBUTE_TYPE ); {Accessor Set Procedure }
begin
F$ATTRIBUTE_NAME := a$ATTRIBUTE_NAME;
end;
]
[{-----------------------------------------------------------------------------------------------}
procedure T$CLASS_NAME$.Set$ASSOCIATION_ONE_CLASS ( a$ASSOCIATION_ONE_CLASS : T$ASSOCIATION_ONE_CLASS );
begin
ASSOCIATION_ONE_NAME := a$ASSOCIATION_ONE_CLASS ;
end;
]
[{-----------------------------------------------------------------------------------------------}
function T$CLASS_NAME$.Get$ASSOCIATION_ONE_CLASS ( ) : T$ASSOCIATION_ONE_CLASS ;
begin
Get$ASSOCIATION_ONE_CLASS := ASSOCIATION_ONE_NAME;
end;
]
[{-----------------------------------------------------------------------------------------------}
procedure T$CLASS_NAME$.Remove$ASSOCIATION_ONE_CLASS ;
begin
ASSOCIATION_ONE_NAME := nil;
end;
]
[{-----------------------------------------------------------------------------------------------}
procedure T$CLASS_NAME$.Set$ASSOCIATION_MANY_CLASS$List ( a$ASSOCIATION_MANY_CLASS$List : TList );
begin
ASSOCIATION_MANY_NAME := a$ASSOCIATION_MANY_CLASS$List ;
end;
]
[{-----------------------------------------------------------------------------------------------}
function T$CLASS_NAME$.Get$ASSOCIATION_MANY_CLASS$List ( ) : TList ;
begin
Get$ASSOCIATION_MANY_CLASS$List := ASSOCIATION_MANY_NAME;
end;
]
[{-----------------------------------------------------------------------------------------------}
procedure T$CLASS_NAME$.Add$ASSOCIATION_MANY_CLASS ( a$ASSOCIATION_MANY_CLASS : T$ASSOCIATION_MANY_CLASS );
begin {Caller must create a$ASSOCIATION_MANY_CLASS }
ASSOCIATION_MANY_NAME.Add (a$ASSOCIATION_MANY_CLASS) ;
end;
]
[{-----------------------------------------------------------------------------------------------}
function T$CLASS_NAME$.Get$ASSOCIATION_MANY_CLASS (Index : Integer ) : T$ASSOCIATION_MANY_CLASS ;
begin
Get$ASSOCIATION_MANY_CLASS := ASSOCIATION_MANY_NAME.Items LITERAL_SYMBOL[Index LITERAL_SYMBOL];
end;
]
[{-----------------------------------------------------------------------------------------------}
function T$CLASS_NAME$.Get$ASSOCIATION_MANY_CLASS$Count : Integer ;
begin
Get$ASSOCIATION_MANY_CLASS$Count := ASSOCIATION_MANY_NAME.Count;
end;
]
[{-----------------------------------------------------------------------------------------------}
procedure T$CLASS_NAME$.Remove$ASSOCIATION_MANY_CLASS ( a$ASSOCIATION_MANY_CLASS : T$ASSOCIATION_MANY_CLASS );
begin { Caller must free a$ASSOCIATION_MANY_CLASS }
ASSOCIATION_MANY_NAME.Remove (a$ASSOCIATION_MANY_CLASS) ; {Caller must free a$ASSOCIATION_MANY_CLASS }
end;
]
[{-----------------------------------------------------------------------------------------------}
procedure T$CLASS_NAME$.RemoveAll$ASSOCIATION_MANY_CLASS ;
begin { Caller must free ASSOCIATION_MANY_CLASS$'s }
ASSOCIATION_MANY_NAME.Clear;
end;
]
end.
Generated Delphi Code for the Car Unit Using DELALL.SCT
unit Car;
interface
uses SysUtils, Messages, Classes , Vehicle , CellularPhone,
Motor, Passenger, Tire;
type
TCar = class (TVehicle)
private
FSpeed : Integer ;
FGasQuantity : Integer ;
CurrentCellularPhone : TCellularPhone ; {1:1 Association Field }
CurrentMotor : TMotor ; {1:1 Aggregation Part Field}
CurrentPassengers : TList ; {1:Many Association Field}
CurrentTires : TList ; {1:Many Aggregation Part Field}
protected
function GetSpeed : Integer; {Get Accessor Function }
function GetGasQuantity : Integer; {Get Accessor Function
}
procedure SetSpeed ( aSpeed : Integer ); {Set Accessor Procedure }
procedure SetGasQuantity ( aGasQuantity : Integer ); {Set
Accessor Procedure }
published
public
procedure Start ; override;
procedure Operate (A_Speed : Integer; A_Number : Integer; var The_Integer : Integer) ;
procedure Stop ;
procedure MakePhoneCall ;
procedure ReceivePhoneCall ;
procedure RegisterIt ; override;
constructor Create; virtual;
{ override; replace virtual with override in derived classes
}
{Update to initialize ancestor fields.}
constructor Initialize ( aSpeed : Integer ; aGasQuantity : Integer );
destructor Destroy ; override;
function EqualTo ( aCar : TCar ) : Boolean ;
property Speed : Integer read GetSpeed;
property GasQuantity : Integer read GetGasQuantity;
procedure SetCellularPhone ( aCellularPhone : TCellularPhone ) ;
{Set Accessor Procedure for 1:1 Association}
function GetCellularPhone : TCellularPhone ;
{Get Assessor Function for 1:1 Association}
procedure RemoveCellularPhone ;
procedure SetPassengerList ( aPassengerList : TList ) ;
{Set Accessor Procedure for 1:Many Association}
function GetPassengerList : TList ;
{Get Assessor Function for 1:Many Association}
procedure AddPassenger ( aPassenger : TPassenger ) ;
function GetPassenger ( Index : Integer ) : TPassenger ;
{Get Assessor Function for 1:Many Association}
function GetPassengerCount : Integer ;
procedure RemovePassenger ( aPassenger : TPassenger ) ;
procedure RemoveAllPassenger;
end;
implementation
{-------------------------------------------------------------------------------}
constructor TCar.Create; {Association Objects Must be Created by Caller}
var
CurrentTiresIndex : Integer;
begin
inherited Create ;
FSpeed := 0;
FGasQuantity := 0;
CurrentCellularPhone := nil; {Caller must create CellularPhone }
CurrentMotor := TMotor.Create;{Create 1:1 Aggregation Part Object}
CurrentPassengers := TList.Create;
{Create List Object for 1:1 Association Objects}
CurrentTires := TList.Create;
{Create List Object for 1:1 Aggregation Part Objects}
for CurrentTiresIndex := 1 to 2 do {Update 2 with the number of Tire's }
CurrentTires.Add (TTire.Create);{Create Tire objects}
end;
{------------------------------------------------------------------------------}
{Update to initialize ancestor fields.}
constructor TCar.Initialize ( aSpeed : Integer ;
aGasQuantity : Integer );
var
CurrentTiresIndex : Integer;
begin
inherited Create; {Update for inherited Initialize with Arguments}
FSpeed := aSpeed;
FGasQuantity := aGasQuantity;
CurrentCellularPhone := nil; {Caller must create CellularPhone }
CurrentMotor := TMotor.Create;{Create 1:1 Aggregation Part Object}
CurrentPassengers := TList.Create;
{Create List Object for 1:1 Association Objects}
CurrentTires := TList.Create;
{Create List Object for 1:1 Aggregation Part Objects}
for CurrentTiresIndex := 1 to 2 do {Update 2 with the number of Tire's }
CurrentTires.Add (TTire.Create);{Create Tire objects}
end;
{-----------------------------------------------------------------------------}
destructor TCar.Destroy ;
var
CurrentTiresIndex : Integer;
TempCurrentTires : TTire ;
begin
CurrentMotor.Free; {Frees 1:1 Aggregation Part Object}
CurrentPassengers.Free;
{Frees 1:Many Association List-caller must free Association objects}
for CurrentTiresIndex := 1 to CurrentTires.Count do
begin
TempCurrentTires := CurrentTires.Items [ CurrentTiresIndex ] ;
TempCurrentTires.Free;
{Frees 1:Many Aggregation Part Objects}
end;
CurrentTires.Free;{Frees 1:Many Aggregation List Object}
inherited Destroy;
end;
{------------------------------------------------------------------------------}
function TCar.EqualTo ( aCar : TCar ) : Boolean;
begin { Does a field by field comparison - update to compare ancestor fields }
EqualTo := True;
if (FSpeed <> aCar.FSpeed) then EqualTo := False ;
if (FGasQuantity <> aCar.FGasQuantity) then EqualTo := False ;
end;
{------------------------------------------------------------------------------}
procedure TCar.Start ;
begin
{ Place code here }
end;
{--------------------------------------------------------------------------------}
procedure TCar.Operate (A_Speed : Integer; A_Number : Integer; var The_Integer : Integer) ;
begin
{ Place code here }
end;
{------------------------------------------------------------------------------}
procedure TCar.Stop ;
begin
{ Place code here }
end;
{------------------------------------------------------------------------------}
procedure TCar.MakePhoneCall ;
begin
{ Place code here }
end;
{------------------------------------------------------------------------------}
procedure TCar.ReceivePhoneCall ;
begin
{ Place code here }
end;
{-----------------------------------------------------------------------------}
procedure TCar.RegisterIt ;
begin
{ Place code here }
end;
{------------------------------------------------------------------------------}
function TCar.GetSpeed : Integer; {Accessor Get Function }
begin
GetSpeed := FSpeed;
end;
{-----------------------------------------------------------------------------}
function TCar.GetGasQuantity : Integer; {Accessor Get Function }
begin
GetGasQuantity := FGasQuantity;
end;
{-----------------------------------------------------------------------------}
procedure TCar.SetSpeed ( aSpeed : Integer ); {Accessor Set Procedure }
begin
FSpeed := aSpeed;
end;
{-----------------------------------------------------------------------------}
procedure TCar.SetGasQuantity ( aGasQuantity : Integer ); {Accessor Set Procedure }
begin
FGasQuantity := aGasQuantity;
end;
{-----------------------------------------------------------------------------}
procedure TCar.SetCellularPhone ( aCellularPhone : TCellularPhone );
begin
CurrentCellularPhone := aCellularPhone ;
end;
{-----------------------------------------------------------------------------}
function TCar.GetCellularPhone ( ) : TCellularPhone ;
begin
GetCellularPhone := CurrentCellularPhone;
end;
{-----------------------------------------------------------------------------}
procedure TCar.RemoveCellularPhone ;
begin
CurrentCellularPhone := nil;
end;
{-----------------------------------------------------------------------------}
procedure TCar.SetPassengerList ( aPassengerList : TList );
begin
CurrentPassengers := aPassengerList ;
end;
{-----------------------------------------------------------------------------}
function TCar.GetPassengerList ( ) : TList ;
begin
GetPassengerList := CurrentPassengers;
end;
{-----------------------------------------------------------------------------}
procedure TCar.AddPassenger ( aPassenger : TPassenger );
begin {Caller must create aPassenger }
CurrentPassengers.Add (aPassenger) ;
end;
{-----------------------------------------------------------------------------}
function TCar.GetPassenger (Index : Integer ) : TPassenger ;
begin
GetPassenger := CurrentPassengers.Items [Index ];
end;
{------------------------------------------------------------------------------}
function TCar.GetPassengerCount : Integer ;
begin
GetPassengerCount := CurrentPassengers.Count;
end;
{------------------------------------------------------------------------------}
procedure TCar.RemovePassenger ( aPassenger : TPassenger );
begin { Caller must free aPassenger }
CurrentPassengers.Remove (aPassenger) ; {Caller must free aPassenger }
end;
{-------------------------------------------------------------------------------}
procedure TCar.RemoveAllPassenger ;
begin { Caller must free Passenger's }
CurrentPassengers.Clear;
end;
end.
Using With Class to Reverse Engineer Delphi Code
After you compile and update your generated Delphi
code in the Delphi environment, you may reverse engineer the code
into a class diagram. Select Utilities - Preferences to select
Delphi. Select Utilities - Reverse Directory. Select the Delphi
files to be reversed. Select OK. With Class reads the Delphi files
and creates the class diagram. You may reverse any Delphi code
that successfully compiles. Below is the reversed class diagram
from the updated code for the Car Simulation program. This diagram
shows the Unified Modeling Language graphic symbols. The trailing
$ indicates an overloaded property/method name.
The following are the steps to reverse Delphi .pas files and to create the data dictionary report.
Generating Delphi from a State Diagram
You may use With Class to create a state diagram and to generate Delphi code from the state diagram. Typically, you will create a single Controller class from the state diagram with a single procedure "Process".
Script to Generate Delphi Code for the Controller
Unit - DELSTAT1.SCT
NO_OUTPUT_BEGIN
State code generation for Delphi from a state diagram
1. Create the state diagram
2. Utilities - Generate State Code/Report
3. Save unit file as Controller.pas
4. Compile Controller.pas in Delphi
NO_OUTPUT_END
SCRIPT_NOREPEAT_HEADER_BEGIN
unit STATE_CLASS_NAME;
interface
uses SysUtils, Messages, Classes ;
type
States = ( [NO_RETURN STATE_LIST_NAME,DELETE_LAST_SYMBOL]
) ;
Events = ( [NO_RETURN EVENT_LIST_NAME,DELETE_LAST_SYMBOL]
) ;
T$STATE_CLASS_NAME = class [(T$BASE_CLASS)]
private
currentState : States;
public
procedure Process ( anEvent : Events
) ;
constructor Create; virtual; {change virtual to override if descendant class}
destructor Destroy; override;
end;
implementation
{-----------------------------------------------------------------------------------------------}
constructor T$STATE_CLASS_NAME$.Create;
begin
inherited Create ;
currentState := STATE_INITIAL_NAME ;
end;
{-----------------------------------------------------------------------------------------------}
{Update as required.}
destructor T$STATE_CLASS_NAME$.Destroy;
begin
inherited Destroy;
end;
{-----------------------------------------------------------------------------------------------}
{ Process procedure }
procedure T$STATE_CLASS_NAME.Process ( anEvent : Events ) ;
var
condition : Boolean;
begin
SCRIPT_NOREPEAT_HEADER_END
[
if ( currentState = TRANSITION_CURRENT_STATE ) And ( anEvent = TRANSITION_EVENT ) And ( TRANSITION_CONDITION )
then
begin
currentState := TRANSITION_NEXT_STATE ;
TRANSITION_ACTION
end;
]
SCRIPT_NOREPEAT_FOOTER_BEGIN
end;
end.
SCRIPT_NOREPEAT_FOOTER_END
Generated Delphi Code for the Controller Unit
Using DELSTAT1.SCT
unit Controller;
interface
uses SysUtils, Messages, Classes ;
type
States = ( OffState, OnState, Termination ) ;
Events = ( turnOff, turnOn ) ;
TController = class
private
currentState : States;
public
procedure Process ( anEvent : Events ) ;
constructor Create; virtual; {change virtual to override if descendant class}
destructor Destroy; override;
end;
implementation
{------------------------------------------------------------------------------}
constructor TController.Create;
begin
inherited Create ;
currentState := OffState ;
end;
{------------------------------------------------------------------------------}
{Update as required.}
destructor TController.Destroy;
begin
inherited Destroy;
end;
{------------------------------------------------------------------------------}
{ Process procedure }
procedure TController.Process ( anEvent : Events ) ;
var
condition : Boolean;
begin
if ( currentState = OffState ) And ( anEvent = turnOn ) And ( True )
then
begin
currentState := OnState ;
end;
if ( currentState = OnState ) And ( anEvent = turnOff ) And ( True )
then
begin
currentState := Termination ;
end;
end;
end.
Reversed Class Diagram from Controller Unit - Controller.pas
Generating Activity Table from an Activity (State)
Diagram
You may use With Class to create an activity diagram
and to generate an activity table from the diagram. As presented
in Unified Modeling Language, the activity diagram is a form of
the state diagram. There are two forms of states: wait state and
activity state. A wait state has an explicit event that causes
a transition. An activity state has an implicit termination event
that causes a transition upon completion of the activity . The
activity diagram is used to describe the steps in a complex operation.
A single activity diagram describes the algorithmic details of
a single complex operation. The activity diagram and activity
table are useful to code complex procedures in Delphi. To create
an activity diagram in With Class, use the State symbol for the
Activity Symbol and enter conditions and actions. In Delphi an
activity may be a single statement, a group of statements, a procedure,
or a function. A condition is an if..then statement. An action
is statement or a message.
Simplified Activity Diagram for TCar::Start
Script to Generate Activity Table from an Activity (State) Diagram
SCRIPT_NOREPEAT_HEADER_BEGIN
Activity Name,Transition Condition,Transition Action,Transition Next Activity SCRIPT_NOREPEAT_HEADER_END
[TRANSITION_CURRENT_STATE,TRANSITION_CONDITION,TRANSITION_ACTION,TRANSITION_NEXT_STATE
]
Activity Table
| Activity Name | Transition Condition | Transition Action | Transition Next Activity |
| CheckGasQuantity | GasQuantity > 0 | Motor1.Start();
User.ShowStatus('Car Started'); | Termination |
| CheckGasQuantity | GasQuantity <= 0 | ProcessException | |
| ProcessException | User.ShowStatus('Error'); | Termination |
Generating Message Table from an Object Interaction
Diagram
You may use With Class to create an object interaction
diagram (sequence diagram) and to generate Delphi messages from
the diagram.
Object Interaction (Sequence) Diagram
Script to Generate Message Table - TABOBMSG.SCT
SCRIPT_NOREPEAT_HEADER_BEGIN
Sequence #, Sender System/Class::Object, Sender Operation, Return Type, Receiver System/Class::Object, Invoked Operation (Parameters) SCRIPT_NOREPEAT_HEADER_END
[INTERACTION_SEQUENCE_NUMBER, INTERACTION_SENDER_CLASS_NAME::INTERACTION_SENDER_OBJECT_NAME,
INTERACTION_SENDER_OPERATION_NAME, INTERACTION_OPERATION_RETURN_TYPE,
INTERACTION_RECEIVER_CLASS_NAME::INTERACTION_RECEIVER_OBJECT_NAME,
INTERACTION_OPERATION_NAME (INTERACTION_OPERATION_PARAMETERS)]
Message Table - Generated Information Placed into
a Table
| Sequence # | Sender System/Class::Object | Sender Operation | Return Type | Receiver System/Class::Object | Invoked Operation (Parameters) |
| 1 | User::User | TCar::Car1 | Start () | ||
| 2 | TCar::Car1 | TCar::Start | TMotor::Motor1 | Start () | |
| 3 | TCar::Car1 | TCar::Start | User::User | ShowStatus () |
Summary Guidelines to Use With Class to Develop
Delphi Applications
The following are some guidelines to create diagrams
and to generate Delphi code.
- Select Utilities - Preferences select Delphi as the language. Enter Delphi types if desired.
- Enter class information in the Class Specification.
- Enter attribute information in the Attribute Specification.
- Enter operation information in the Operation Specification.
- To generate Delphi code, select a Delphi script, e.g. DELBASIC.SCT.
- After changing the Delphi code, import the changed code from the Operation Specification - Code Form.
- To create a class diagram from existing Delphi code, select Utilities - Reverse Directory.
- Additionally, Delphi code may be generated from
a state diagram with the script DELSTAT1.SCT and a Delphi message
table may be generated from an object interaction diagram with
the script TABOBMSG.SCT.
Comments and suggestions on this tutorial and the Delphi code generation scripts are requested - Richard Felsinger RCF Associates, 960 Scottland Dr, Mt Pleasant, SC 29464 Telephone 803-881-3648 e-mail 71162.755@compuserve.com Comprehensive O-O Model Building and C++/Java/Delphi Development and Training Available.