# Object-Oriented PxPlus

Putting It All Together |

The example below describes the creation and implementation of three objects in PxPlus (Company, Customer, Supplier) to illustrate OOP syntax (see PxPlus OOP Interface).

## Company Class

The contents of the company.pvc class definition file is as follows:

> 0010 DEF CLASS "Company"
> 0020 PROPERTY Name$,Address$,City$,PhoneNumber$,FaxNumber$
> 0030 FUNCTION Add()Add
> 0040 FUNCTION Delete()Delete
> 0050 FUNCTION Fax()Fax
> 0060 FUNCTION ShowSpecial()";ShowSpecial"
> 0070 END DEF
> 0100 ! ^100
> 0110 On_Create:; GOSUB Where_are_we; RETURN 1
> 0120 On_Delete:; GOSUB Where_are_we; RETURN 1
> 0200 ! ^100
> 0210 Add:; GOSUB Where_are_we; RETURN 1
> 0220 Delete:; GOSUB Where_are_we; RETURN 1
> 0230 Edit:; GOSUB Where_are_we; RETURN 1
> 0240 Find:; GOSUB Where_are_we; RETURN 1
> 0250 Fax:; GOSUB Where_are_we; RETURN 1
> 0300 ! ^100
> 0310 ShowSpecial:
> 0320 X$="Special Variables that exist in every Object:"+SEP
> 0330 X$+=$09$+"_OBJ = "+STR(_OBJ)+SEP+$09$
> 0340 X$+="_CLASS$ = "+_CLASS$+SEP+$09$
> 0350 X$+="_REFCNT = "+STR(_REFCNT)+SEP
> 0360 MSGBOX X$,"In the Company "+FNLabelName$+" logic"
> 0370 RETURN 1
> 0400 ! ^100
> 0410 Where_are_we:
> 0420 MSGBOX "In the Company "+FNLabelName$+" logic","FYI"
> 0430 RETURN
> 8000 ! 8000
> 8010 def fnLabelName$=mid(pgm(-3),pos(";"=pgm(-3)+";")+1)

The definition of this class starts with the DEF CLASS directive on statement 0010 and concludes with the END DEF directive on statement 0070. All of the fields (Properties) of the object are declared by the PROPERTY directive on statement 0020. The Methods available are defined using the FUNCTION directive on statements 0030 through 0060.

Creating a reference to or instantiating the class in an application is accomplished using the NEW( ) function; i.e. Comp=NEW("Company"). The numeric variable Comp contains a reference to the object known as an Object Identifier. All interaction with the object takes place using this object identifier.

Creating a new object automatically executes the logic that follows the statement label called On_Create in the program associated with the object. This label is optional and does not generate an error if it does not exist.

Determining what properties and methods are available for a given object is accomplished using the apostrophe operator (' tick) followed by an * asterisk. See Apostrophe Operator.

Example 1:

> print Comp'* ! Show all Properties & Methods
> ! Produces the following results:
> FaxNumber$,PhoneNumber$,City$,Address$,Name$,ShowSpecial(),Fax(), Delete(),Add(),

The properties are listed as simple string or numeric variable names while the available methods end with a pair of parentheses "( )".

Methods are called by specifying the object identifier and the apostrophe operator, followed by the method name. As methods are designed to return a status value, they must appear on the right side of a LET assignment or be used with a directive such as a PRINT.

Example 2:

> print Comp'ShowSpecial()
> ! Displays the following status value
> 1

This produces a message box that lists all of the special variables that are available while executing code on behalf of an object; i.e. _Obj, _Class$ and _Refcnt.

After acknowledging the message box, a value of 1 (one) appears on the next line. This value represents the status returned by the method when it executes the RETURN 1 on statement 0370. A non-zero status value usually indicates that a method is successful, while a value of zero signifies that it is not (although this is at the discretion of the programmer).

Objects must be dropped or deleted when the application is finished with them. This is accomplished using DROP OBJECT or REF( ) techniques:

> DROP OBJECT obj_id
> VARIABLE = REF(DROP obj_id)

The following example removes the Company object just created:

> Drop Object Comp

Removing an object results in the On_Delete statement label being executed (if it exists). In the Company object example, a message box will appear that indicates "In the Company On_Delete logic".

## Customer Class

The contents of the customer.pvc class definition file is as follows:

> 0010 DEF CLASS "Customer"
> 0020 LIKE "Company"
> 0030 PROPERTY Limit,LastInvDate$
> 0040 FUNCTION Invoice()";Invoice"
> 0050 FUNCTION Edit()";Edit"
> 0060 END DEF
> 0100 ! ^100
> 0110 On_Create:; GOSUB Where_are_we; RETURN 1
> 0120 On_Delete:; GOSUB Where_are_we; RETURN 1
> 0200 ! ^100
> 0210 Invoice:; GOSUB Where_are_we; RETURN 1
> 0220 Edit:; GOSUB Where_are_we; RETURN 1
> 0300 ! ^100
> 0310 Where_are_we:
> 0320 MSGBOX "In the Customer "+FNLabelName$+" logic","FYI"
> 0330 RETURN
> 8000 ! 8000
> 8010 def fnlabelname$=mid(pgm(-3),pos(";"=pgm(-3)+";")+1)

This class definition introduces the Inheritance aspect of object-oriented programming by its use of the LIKE directive on line 0020. LIKE is used to include all of the characteristics from a different object into the current object. Creating an object using this class definition and viewing all of its properties and methods best illustrates this.

Example 1:

> Cust=NEW("Customer")
> print Cust'*
> ! Produces the following results:
> LastInvDate$,Limit,Edit(),Invoice(),FaxNumber$,PhoneNumber$,City$,Address$,Name$,ShowSpecial(),Fax(),Delete(),Add(),

The Customer class explicitly defines the LastInvDate$ and Limit properties and the Invoice() and Edit () methods. The remaining properties (City$, Address$, etc.) and methods (ShowSpecial(), Fax(), etc.) are inherited from the Company class. This allows classes that are related by inheritance to share common properties and methods without having to duplicate any of the programming code normally required. Having the code in a single location helps to reduce the amount of time spent on maintenance and enhancements.

The Where_are_we sub-routine used in both the Company and Customer classes could be converted to a method, which would allow both to share the same code. To simplify the examples, this approach is not taken.

From an application standpoint, there is no difference between accessing a method defined in the base class or one that is inherited.

Example 2:

> Status = Cust'Fax()

Although the Customer class does not have a Fax() method, it is available since it is inherited from the Company class. Remember to delete the instance of Customer (Cust object) when finished:

> Drop Object Cust

## Supplier Class

The contents of the supplier.pvc class definition file is as follows:

> 0010 def class "Supplier"
> 0020 like "Company"
> 0030 property MinOrder,LeadTime
> 0040 function Order()";Order"
> 0050 function Edit()";Edit"
> 0060 end def
> 0100 ! ^100
> 0110 On_Create:; gosub Where_are_we; return 1
> 0120 On_Delete:; gosub Where_are_we; return 1
> 0200 ! ^100
> 0210 Order:; gosub Where_are_we; return 1
> 0220 Edit:; gosub Where_are_we; return 1
> 0300 ! ^100
> 0310 Where_are_we:
> 0320 msgbox "In the Supplier "+fnLabelName$+" logic","FYI"
> 0330 return
> 8000 ! 8000
> 8010 def fnLabelName$=mid(pgm(-3),pos(";"=pgm(-3)+";")+1)

The basic concepts illustrated with the Customer class also apply to Supplier.

## Additional Classes - Examples

In the following sections, a few changes were made to the Customer class mentioned above. Each of these samples will introduce some additional features of PxPlus OOP syntax: Cust2, Cust3, Cust4, Cust5 and Cust6.

A simple data file called cstfile is used in these classes. The following tables outline the required information to create this file.

Field Definitions

Field Name | Description | Type | Len | Delimited?
Cust_No$ | Customer ID | String | 6 | Yes
Name$ | Customer Name | String | 30 | Yes
Addr$ | Address | String | 30 | Yes
City$ | City | String | 30 | Yes
Salesrep$ | Sales Rep | String | 3 | Yes
Amt_Owing | Amount Owing | Numeric | 10.2 | Yes

Key Definitions

Key # | Description | Fields
0 | Primary Key | Cust_No$
1 | Sort by Name | Cust_Name$ + Cust_No$
2 | Sort by Sales Rep | Salesrep$ + Cust_No$

For the final two sample objects, a NOMADS File Maintenance panel for the Customer file (Cstupd) is assumed.

Cust2 Class

The contents of the cust2.pvc class definition file illustrate the ability to associate program logic with a property when an attempt is made to alter its contents.

> 00010 DEF CLASS "Cust2"
> 00020 PROPERTY Cust_No$ SET Change_Cust
> 00030 PROPERTY Name$,Addr$,City$,Salesrep$
> 00040 PROPERTY Amt_Owing SET ERR
> 00050 FUNCTION Find(X$)Get_Customer
> 00060 FUNCTION Next()Read_Next
> 00070 FUNCTION Update()Update_Customer
> 00080 END DEF
> 00100 ! !^100
> 00110 On_Create:
> 00120 IF %Customer_File=0 \
> THEN OPEN (GFN,IOL=*)"cstfile";
> %Customer_File=LFO
> 00130 %Customer_Object++
> 00140 RETURN
> 00200 ! !^100
> 00210 Get_Customer: \
> ENTER C$
> 00220 READ (%Customer_File,KEY=C$) ! Loads all the variables
> 00230 RETURN 1
> 00300 ! !^100
> 00310 Change_Cust: \
> ENTER C$
> 00320 READ DATA FROM "" TO IOL=IOL(%Customer_File)
> 00330 Cust_No$=C$
> 00340 READ (%Customer_File,KEY=C$,DOM=*END)
> 00350 RETURN
> 00400 ! !^100
> 00410 Read_Next:
> 00420 READ (%Customer_File,END=*NEXT);
> RETURN 1
> 00430 RETURN 0
> 00500 ! !^100
> 00510 Update_Customer:
> 00520 WRITE (%Customer_File)
> 00530 RETURN 1
> 00600 ! !^100
> 00610 On_Delete:
> 00620 IF --%Customer_Object<>0 \
> THEN RETURN
> 00630 CLOSE (%Customer_File);
> %Customer_File=0
> 00640 RETURN

A PROPERTY definition can include an optional keyword SET, followed by either a line label or the keyword ERR. In the case of a line label, the program logic starting at that label is executed when an attempt is made to alter the contents of the property. The keyword ERR is used to prevent the contents of a property from being changed, and an error is returned to the program attempting to do so. By default, an Error #88: Invalid/unknown property name is returned. However, this can be overridden as shown in a subsequent sample class.

These options are used on the following property declarations:

> 0020 property Cust_No$ set Change_Cust
> 0040 property Amt_Owing set err

To illustrate use of this class, create a new instance of Cust2:

> Cust2=NEW("Cust2")

Auto-execute the Change_Cust method by setting the Cust_No$ variable. Use SETTRACE/ENDTRACE to show the code executing:

> SETTRACE
> Cust2'Cust_No$="000011"
> 0310 Change_Cust: ENTER C$
> 0320 READ DATA FROM "" TO IOL=IOL(%Customer_File)
> 0330 LET Cust_No$=C$
> 0340 READ (%Customer_File,KEY=C$,DOM=*END)
> 0350 RETURN
> ENDTRACE

If you try to change the Amt_Owing property, this results in an Error #88 due to the SET ERR clause on the property definition:

> Cust2'Amt_Owing=100
> Error #88: Invalid/unknown property name

Drop the object when finished:

> DROP OBJECT Cust2

#### Note:
One key issue regarding Cust2 is the use of global variables, which results in a violation of the principle of Encapsulation. PxPlus itself has no mechanism to enforce OOP "rules". Cust3 below shows a way to avoid this problem.

Cust3 Class

This class illustrates a better approach for handling Encapsulation - by replacing the global variables with local variables. It also introduces the ability to associate program logic when reading the contents of a property. The contents of the cust3.pvc class definition file appear as follows:

> 00010 DEF CLASS "Cust3"
> 00020 LOCAL Customer_File,Customer_Object
> 00030 PROPERTY Cust_No$ SET Change_Cust
> 00040 PROPERTY Name$,Addr$,City$,Salesrep$
> 00050 PROPERTY Amt_Owing GET ";CheckSecurity" SET ERR
> 00060 FUNCTION Find(X$)Get_Customer
> 00070 FUNCTION Next()Read_Next
> 00080 FUNCTION Update()Update_Customer
> 00090 END DEF
> 00100 !
> 00200 ! ^100
> 00210 On_Create:
> 00220 IF Customer_File=0 \
> THEN OPEN (GFN,IOL=*)"cstfile";
> Customer_File=LFO
> 00230 Customer_Object++
> 00240 RETURN
> 00300 ! ^100
> 00310 Get_Customer: \
> ENTER C$
> 00320 READ (Customer_File,KEY=C$) ! Loads all the variables
> 00330 RETURN 1
> 00500 ! ^100
> 00510 Change_Cust: \
> ENTER C$
> 00520 READ DATA FROM "" TO IOL=IOL(Customer_File)
> 00530 Cust_No$=C$
> 00540 READ (Customer_File,KEY=C$,DOM=*END)
> 00550 RETURN
> 00600 ! ^100
> 00610 Read_Next:
> 00620 READ (Customer_File,END=*NEXT);
> RETURN 1
> 00630 RETURN 0
> 00700 ! ^100
> 00710 Update_Customer:
> 00720 WRITE (Customer_File)
> 00730 RETURN 1
> 00800 ! ^100
> 00810 CheckSecurity:
> 00820 IF %Security_OK \
> THEN RETURN Amt_Owing \
> ELSE EXIT 52
> 00900 ! ^100
> 00910 On_Delete:
> 00920 IF --Customer_Object<>0 \
> THEN RETURN
> 00930 CLOSE (Customer_File);
> Customer_File=0
> 00940 RETURN

Cust3 replaces the global variables %Customer_File and %Customer_Object with properties that are local to the object. This is accomplished with the use of the LOCAL directive:

> 0020 LOCAL Customer_File,Customer_Object

Properties declared LOCAL are available when executing logic on behalf of an object, yet they are invisible to the outside world.

While Cust2 demonstrated the ability to intercept any attempt made to change a value, Cust3 introduces the syntax required to intercept any attempts made to read a property. This is accomplished by adding the keyword GET to the PROPERTY definition:

> 0050 property Amt_Owing get ";CheckSecurity" set err

The CheckSecurity() method in Cust3 is designed to return the value of the Amt_Owing property if a global variable, %Security_OK is set to a non-zero value. Otherwise, it returns an Error #52: Program is password protected.

To illustrate use of this class, create a new instance of Cust3:

> Cust3=NEW("Cust3")

Get the next customer on file with the Next() method:

> x=Cust3'Next()

Disable the security override flag:

> %Security_OK=0

An attempt made to query the Amt_Owing property will result in an Error #52 due to the EXIT 52 on statement 0820:

> PRINT Cust3'Amt_Owing
> Error #52: Program is password protected

Enable the security override flag:

> %Security_OK=1

Display the Amt_Owing property after enabling SETTRACE and ENDTRACE to show the code executing:

> SETTRACE
> PRINT Cust3'Amt_Owing
> 0810 CheckSecurity:
> 0820 IF %Security_OK THEN RETURN Amt_Owing ELSE EXIT 52
> 0
> ENDTRACE

Drop the object when finished:

> DROP OBJECT Cust3

Cust4 Class

This class introduces the concept of Method Overloading, which is the ability to reference a method with a varying number and/or type of parameters. This technique simplifies the external interface to the object by having a single method name. One example of this would be the ability to find a customer using either a string or numeric value. The contents of the cust4.pvc class definition file appear as follows:

> 00010 DEF CLASS "Cust4"
> 00020 LOCAL Customer_File,Customer_Object
> 00030 PROPERTY Cust_No$ SET Change_Cust
> 00040 PROPERTY Name$,Addr$,City$,Salesrep$
> 00050 PROPERTY Amt_Owing GET ";CheckSecurity" SET ERR
> 00060 FUNCTION Find(X$)Get_Customer
> 00070 FUNCTION Find(X)Get_Customer_Numeric
> 00080 FUNCTION Next()Read_Next
> 00090 FUNCTION Update()Update_Customer
> 00100 END DEF
> 00200 ! ^100
> 00210 On_Create:
> 00220 IF Customer_File=0 \
> THEN OPEN (GFN,IOL=*)"cstfile";
> Customer_File=LFO
> 00230 Customer_Object++
> 00240 RETURN
> 00300 ! ^100
> 00310 Get_Customer: \
> ENTER C$
> 00320 READ (Customer_File,KEY=C$) ! Loads all the variables
> 00330 RETURN 1
> 00400 ! ^100
> 00410 Get_Customer_Numeric: \
> ENTER C
> 00420 C$=STR(C:"000000",ERR=*NEXT)
> 00430 C$=PAD(UCS(C$),6)
> 00440 READ (Customer_File,KEY=C$) ! Loads all the variables
> 00450 RETURN 1
> 00500 ! ^100
> 00510 Change_Cust: \
> ENTER C$
> 00520 READ DATA FROM "" TO IOL=IOL(Customer_File)
> 00530 Cust_No$=C$
> 00540 READ (Customer_File,KEY=C$,DOM=*END)
> 00550 RETURN
> 00600 ! ^100
> 00610 Read_Next:
> 00620 READ (Customer_File,END=*NEXT);
> RETURN 1
> 00630 RETURN 0
> 00700 ! ^100
> 00710 Update_Customer:
> 00720 WRITE (Customer_File)
> 00730 RETURN 1
> 00800 ! ^100
> 00810 CheckSecurity:
> 00820 IF %Security_OK \
> THEN RETURN 1 \
> ELSE EXIT 52
> 00900 ! ^100
> 00910 On_Delete:
> 00920 IF --Customer_Object<>0 \
> THEN RETURN
> 00930 CLOSE (Customer_File);
> Customer_File=0
> 00940 RETURN

Notice that FUNCTION statements for the Find( ) method appear twice:

> 0060 FUNCTION Find(X$)Get_Customer
> 0070 FUNCTION Find(X)Get_Customer_Numeric

The significant difference between these two definitions is the type of parameter specified in the parentheses. The first accepts a string value, while the second requires a numeric entry.

Multiple definitions of the same method can be specified, provided each has a different parameter list. Knowing which method definition to use is determined by matching the parameters based on their type, either string or numeric, and the number of parameters specified.

To illustrate use of this class, create a new instance of Cust4:

> Cust4=NEW("Cust4")

Locate the first customer using a string argument:

> x=Cust4'Find("000011")
> PRINT Cust4'Cust_No$," - ",Cust4'Name$
> 000011 - Flagship Importers

Locate the second customer using a numeric argument:

> x=Cust4'Find(27)
> PRINT Cust4'Cust_No$," - ",Cust4'Name$
> 000027 - Yorktown Wood Products

Drop the object when finished:

> DROP OBJECT Cust4

Panel Class

This class will be used with the Cust5 example. It assumes a NOMADS File Maintenance panel called Cstupd has been created for the Customer file. The contents of the panel.pvc class definition file appear as follows:

> 00010 ! Panel Definition
> 00020 DEF CLASS "Panel"
> 00030 LOCAL Panel_Name$,Panel_Library$
> 00040 FUNCTION Edit()Do_Panel
> 00050 END DEF
> 00060 Do_Panel:
> 00070 IF NUL(Panel_Name$) \
> THEN Panel_Name$=_OBJ'_CLASS$
> 00080 IF NUL(Panel_Library$) \
> THEN Panel_Library$="PxPlus.en"
> 00090 Sv_prc=PRC;
> RESET ;
> PRECISION Sv_prc
> 00100 Scrn_ID$=Panel_Name$,Scrn_Lib$=Panel_Library$
> 00110 PERFORM "*winproc;Post_Enter"
> 00120 RETURN 1

File Class

This class will be used with the Cust5 example. The contents of the file.pvc class definition file appear as follows:

> 00010 ! "File" class definition
> 00020 DEF CLASS "File"
> 00030 LOCAL FileNo
> 00040 FUNCTION Open(FILENAME$)Open_File
> 00050 FUNCTION Close()Close_File
> 00060 FUNCTION Find(K$)Read_Via_Key
> 00070 FUNCTION Find(K$,KeyNo)Read_Via_Alt_Key
> 00080 FUNCTION Find(K$,KeyNo$)Read_Via_Alt_Key2
> 00090 FUNCTION Find(I)Read_Via_Ind
> 00100 FUNCTION Next()Read_Next
> 00110 FUNCTION Next(KeyNo)Read_Next_Alt_Key
> 00120 FUNCTION Next(KeyNo$)Read_Next_Alt_Key2
> 00130 FUNCTION Update()Write_Record
> 00140 FUNCTION Update(K)Write_Record_With_Key
> 00150 FUNCTION Insert()Insert_Record
> 00160 FUNCTION Insert(K$)Insert_Record_With_Key
> 00170 FUNCTION Valid()=1 ! Always true unless overridden
> 00180 END DEF
> 00200 ! ^100 Open
> 00210 Open_File:
> 00220 ENTER F$
> 00230 IF FileNo<>0 \
> THEN EXIT 13
> 00240 X=HFN;
> OPEN (X,IOL=*)F$
> 00250 FileNo=X;
> RETURN 1
> 00300 ! ^100 Close
> 00310 Close_File:
> 00320 IF FileNo=0 \
> THEN EXIT 13
> 00330 CLOSE (FileNo);
> FileNo=0
> 00340 RETURN 1
> 00400 ! ^100 Read via Index
> 00410 Read_Via_Ind:
> 00420 ENTER I
> 00430 READ DATA FROM "" TO IOL=IOL(FileNo)
> 00440 READ (FileNo,IND=I,DOM=*NEXT,END=*NEXT);
> RETURN 1
> 00450 RETURN 0
> 00500 ! ^100 Read via primary key
> 00510 Read_Via_Key:
> 00520 ENTER K$
> 00530 READ DATA FROM "" TO IOL=IOL(FileNo)
> 00540 READ (FileNo,KEY=K$,KNO=0,DOM=*NEXT);
> RETURN 1
> 00550 RETURN 0
> 00600 ! ^100 Read via an alternate key
> 00610 Read_Via_Alt_Key:
> 00620 ENTER K$,KeyNo
> 00630 READ DATA FROM "" TO IOL=IOL(FileNo)
> 00640 READ (FileNo,KEY=K$,KNO=KeyNo,DOM=*NEXT);
> RETURN 1
> 00650 RETURN 0
> 00700 ! ^100 Read via an alternate key
> 00710 Read_Via_Alt_Key2:
> 00720 ENTER K$,KeyNo$
> 00730 READ DATA FROM "" TO IOL=IOL(FileNo)
> 00740 READ (FileNo,KEY=K$,KNO=KeyNo$,DOM=*NEXT);
> RETURN 1
> 00750 RETURN 0
> 00800 ! ^100 Read Next on primary key
> 00810 Read_Next:
> 00820 READ DATA FROM "" TO IOL=IOL(FileNo)
> 00830 READ (FileNo,KNO=0,END=*NEXT);
> RETURN 1
> 00840 RETURN 0
> 00900 ! ^100 Read Next via an alternate key
> 00910 Read_Next_Alt_Key:
> 00920 ENTER KeyNo
> 00930 READ DATA FROM "" TO IOL=IOL(FileNo)
> 00940 READ (FileNo,KNO=KeyNo,END=*NEXT);
> RETURN 1
> 00950 RETURN 0
> 01000 ! ^100 Read Next via an alternate key
> 01010 Read_Next_Alt_Key2:
> 01020 ENTER KeyNo$
> 01030 READ DATA FROM "" TO IOL=IOL(FileNo)
> 01040 READ (FileNo,KNO=KeyNo$,END=*NEXT);
> RETURN 1
> 01050 RETURN 0
> 01100 ! ^100 - Update
> 01110 Write_Record:
> 01120 IF NOT(_OBJ'Valid()) \
> THEN EXIT 17
> 01130 WRITE (FileNo);
> RETURN 1
> 01140 !
> 01150 Write_Record_With_Key:
> 01160 ENTER K$
> 01170 IF NOT(_OBJ'Valid()) \
> THEN EXIT 17
> 01180 WRITE (FileNo,KEY=K$);
> RETURN 1
> 01200 ! ^100 - Insert record
> 01210 Insert_Record:
> 01220 IF NOT(_OBJ'Valid()) \
> THEN EXIT 17
> 01230 WRITE (FileNo,DOM=*NEXT);
> RETURN 1
> 01240 RETURN 0
> 01250 !
> 01260 Insert_Record_With_Key:
> 01270 ENTER K$
> 01280 IF NOT(_OBJ'Valid()) \
> THEN EXIT 17
> 01290 WRITE (FileNo,KEY=K$,DOM=*NEXT);
> RETURN 1
> 01300 RETURN 0

Cust5 Class

This class illustrates the ability to inherit the characteristics of additional classes. The contents of the cust5.pvc class definition file appear as follows:

> 00010 DEF CLASS "Cust5"
> 00020 LIKE "File","Panel"
> 00030 PROPERTY Cust_No$ WRITE Change_Cust
> 00040 PROPERTY Name$,Addr$,City$,Salesrep$,Amt_Owing
> 00050 END DEF
> 00060 !
> 00070 On_Create:
> 00080 Panel_Library$="cstfile.en",Panel_Name$="cstupd"
> 00090 RETURN _Obj'Open("cstfile")
> 00100 !
> 00110 On_Delete:
> 00120 RETURN _Obj'Close()
> 00130 !
> 00140 Change_Cust: \
> ENTER C$
> 00150 IF NOT(_Obj'Find(C$)) \
> THEN Cust_No$=C$
> 00160 RETURN 1

While the definition is relatively small, this class is more powerful than it looks due to the inclusion of File and Panel classes provided earlier. Referencing methods and properties from inherited classes is accomplished by prefixing the method name with _Obj, as illustrated in the following statements:

> 0090 RETURN _Obj'Open("cstfile")
> 0120 RETURN _Obj'Close()
> 0150 IF NOT(_Obj'Find(C$)) THEN Cust_No$=C$

In each case, prefixing the method name with _Obj (in place of the object identifier) indicates that the method exists somewhere in the current class. Knowing its exact location is not necessary. See Using Methods and Properties. The example below uses the Edit() method from the Panel object to process a NOMADS panel.

To illustrate use of this class, create a new instance of Cust5:

> Cust5=NEW("Cust5")

Display the available properties and methods:

> PRINT Cust5'*
> Addr$,Amt_Owing,City$,Close(),Cust_No$,Edit(),Find(),Insert(),Name$,Next(),Open(),Salesrep$,Update(),Valid(),

Invoke a NOMADS panel:

> x=Cust5'Edit()

Drop the object when finished:

> Drop Object Cust5

Cust6 Class

This class demonstrates the Polymorphism aspect of object orientation. It also introduces the concept of Method Overriding, which is the ability for a child class to override methods (same name and argument list) with different functionality. The contents of the cust6.pvc class definition file appear as follows:

> 00010 DEF CLASS "Cust6"
> 00020 LIKE "File","Panel"
> 00030 PROPERTY Cust_No$ WRITE Change_Cust
> 00040 PROPERTY Name$,Addr$,City$,Salesrep$,Amt_Owing
> 00050 FUNCTION Find(X$)Do_MyFind
> 00060 END DEF
> 00070 !
> 00080 On_Create:
> 00090 Panel_Library$="cstfile.en",Panel_Name$="cstupd"
> 00100 RETURN _Obj'Open("cstfile")
> 00110 !
> 00120 On_Delete:
> 00130 RETURN _Obj'Close()
> 00140 !
> 00150 Change_Cust: \
> ENTER C$
> 00160 IF NOT(_Obj'Find(C$)) \
> THEN Cust_No$=C$
> 00170 RETURN 1
> 00180 !
> 00190 Do_MyFind:
> 00200 ENTER K$
> 00210 K$=STR(NUM(K$,ERR=*NEXT):"000000")
> 00220 K$=PAD(K$,6)
> 00230 RETURN _Obj'Find(FROM "File",K$)

The Find( ) method declared in Cust6 is designed to augment the functionality of the Find( ) method from the File class. The Find( ) method in this class intercepts the value, reformats it to match the key value for the data file, and then passes the resulting value to the Find( ) method in the File class.

To illustrate use of this class, create a new instance of Cust6:

> Cust6=NEW("Cust6")

Pass what appears to be an invalid Customer ID to the Find( ) method:

> x=Cust6'Find("67")
> PRINT "Customer: ",Cust6'Cust_No$," - ",Cust6'Name$
> Customer: 000011 - Flagship Importers

Drop the object when finished:

> Drop Object Cust6