                                                                             OOP
                                                                      Principles





         "The human mind treats a new idea the way the body treats a strange
         protein; it rejects it."

                                                        P. B. Medawar, Biologist




         Part 2 of the User's manual explains how, by using object oriented
         programming, you can customize the Toolkit to meet your very specific
         needs. In this chapter, some of the main features of OOP extensibility
         are explained. If you are an experienced OOPer, you may want to skip
         this chapter and proceed with the Toolkit specific text.

         If you are new to OOP, be sure to read the Object Primer starting on
         page 3.1 before you look at this chapter.



Understanding Inheritance

         One of the most powerful and alluring aspects of object oriented pro-
         gramming is the ability to modify and expand an object without changing
         any of the object's source code. What's more, it's easy to do! The
         basic approach is to create an object which inherits all the properties
         of another object, i.e. you clone the object (sorry Compaq). You can
         then change some of the properties of the cloned object so that it
         functions the way you want it to.

         This feature allows you to enhance and improve the Toolkit without
         changing a single line of Toolkit source code. That way, when we
         release a new and improved version, you don't have to wrestle with two
         sets of source code - the code that you changed and the code that we
         changed. All you need to do is "plug in" the new Toolkit and your
         objects will automatically inherit the new features added to the Tool-
         kit.

         To illustrate object inheritance, let's solve a practical problem.
         Imagine that you use the EquipOBJ object, but need to enhance it so
         that it will report if a CD-ROM is installed. Rather than change the
         EquipOBJ object, the approach is to create a new object which inherits
         all the properties of EquipOBJ, and add the new CD-ROM method to it.

         You may recall from chapter 3: Toolkit Basics that declaring an object
         is very similar to declaring a record. To declare a object that inher-
         its the properties of another object, you specify the name of the par-
         ent object in parentheses after the object keyword. The following code
         fragment declares a new object type, NewEquipOBJ, which inherits all
         the properties of EquipOBJ:

         NewEquipOBJ = object (EquipOBJ)
         end; {NewEquipOBJ}

16-2                                                       Extending the Toolkit

--------------------------------------------------------------------------------

         If you create an object which is a descendant of a Toolkit object, you
         must define two special methods known as the CONSTRUCTOR and the
         DESTRUCTOR. Furthermore, these should be named INIT and DONE, respec-
         tively. These special methods instruct Turbo Pascal to use special
         memory techniques when creating and disposing of the object. The
         NewEquipOBJ object declaration must be expanded to include the con-
         structor and destructor methods as follows:

         NewEquipOBJ = object (EquipOBJ)
            constructor Init;
            destructor  Done;
         end; {NewEquipOBJ}

         Now we can add the new CD-ROM method to the object declaration.  Assum-
         ing that the new method is just a boolean function, it would be
         declared as follows:

         NewEquipOBJ = object (EquipOBJ)
            constructor Init;
            function    CDROM: boolean;
            destructor  Done;
         end; {NewEquipOBJ}

         As well as adding new methods to the object, you can override or change
         existing methods. For example, the GameAdapter function inherited from
         EquipOBJ can be replaced with a new method by expanding the object
         declaration as follows:

         NewEquipOBJ = object (EquipOBJ)
            constructor Init;
            function    CDROM: boolean;
            function    GameAdapter: boolean;
            destructor  Done;
         end; {NewEquipOBJ}

         Although you have only declared NewEquipOBJ with four methods, it
         actually includes all the data and methods it has inherited from Equi-
         pOBJ. The following table compares the data and methods declared in
         EquipOBJ, with the data and methods in NewEquipOBJ. The non-bold items
         are inherited.



                       EquipOBJ                          NewEquipOBJ
          vMainInfo: integer;                vMainInfo: integer;
          vComputerID: byte;                 vComputerID: byte;
          vRomDate: string[8];               vRomDate: string[8];
          constructor Init;                  constructor Init;
          function    ComputerID:byte;       function    ComputerID:byte;
          function    ParallelPorts:byte;    function    ParallelPorts:byte;
          function    SerialPorts:byte;      function    SerialPorts:byte;
          function    FloppyDrives:byte;     function    FloppyDrives:byte;




OOP Principles                                                              16-3

--------------------------------------------------------------------------------

          function    ROMDate:string;        function    ROMDate:string;
          function    SerialPrinter:boolean; function    SerialPrinter:boolean;
          function    MathChip:boolean;      function    MathChip:boolean;
          function    GameAdapter:boolean;   function    GameAdapter:boolean;
          destructor  Done;                  function    CDROM:boolean;
                                             destructor  Done;





           Note: an inherited object may also include data items, provided
           the identifiers (or variable names) are different from the ones
           inherited from the parent object.




         The object declaration defines a new object type and should be located
         in the TYPE declaration section of your program or unit. The corre-
         sponding detail of each object method must be listed either in the body
         of your program, or in the implementation section of a unit.

         Listed below is a unit, EXTDEM1.PAS, which fully defines the new object
         NewEquipOBJ. One of the important points to note is that the construc-
         tor and destructor methods must call the associated constructor and
         destructor from the parent method. This ensures that the inherited data
         is initialized and disposed of properly. These important statements are
         highlighted in bold in the listing.



         Unit ExtDem1;

         INTERFACE

         Uses DOS,CRT, totSYS;

         TYPE

         NewEquipOBJ = object (EquipOBJ)
            constructor Init;
            function    CDROM: boolean;
            function    GameAdapter: boolean;
            destructor  Done;
         end; {NewEquipOBJ}

         IMPLEMENTATION

         constructor NewEquipOBJ.Init;
         {}
         begin
            EquipOBJ.Init;
         end; {NewEquipOBJ.Init}


16-4                                                       Extending the Toolkit

--------------------------------------------------------------------------------

         function NewEquipOBJ.CDROM:boolean;
         {If you know how to do this - please tell us!
          For now, we'll assume one isn't installed}
         begin
            CDROM := false;
         end; {NewEquipOBJ.CDROM}

         function NewEquipOBJ.GameAdapter:boolean;
         {}
         begin
            GameAdapter := paramstr(1) = '/G';
         end; {NewEquipOBJ.GameAdapter}

         destructor NewEquipOBJ.Done;
         {}
         begin
            EquipOBJ.Done;
         end; {NewEquipOBJ.Done}

         end.


         The body of the CD-ROM and GameAdapter methods would normally contain
         your custom code. In this case, their value is limited. But you get the
         idea!

         Take a look at the object hierarchy diagrams at the beginning of the
         Flash Cards. They show the Toolkit's parent-sibling object relation-
         ships. For example, ScrollWinOBJ inherits MoveWinOBJ, which, in turn,
         inherits WinOBJ.



Mastering Extensibility

         All the methods in the EquipOBJ object, discussed in the last section,
         are known as static methods. Static methods are usually independent
         methods, i.e. methods which do not call other methods in the object.
         For example, the GameAdapter method has no relationship with the Math-
         Chip method or any other EquipOBJ method.

         Inheritance becomes somewhat more complicated when methods call other
         methods from the same object. The problem is best illustrated by exam-
         ple. The unit listing below contains an object, PrintOBJ, which is used
         to print strings or integers to the printer connected to LPT1. The
         objective is to create a descendant object which can print to any
         printer port.

         Unit BadPrint;

         INTERFACE

         Uses DOS,CRT, totSTR;



OOP Principles                                                              16-5

--------------------------------------------------------------------------------

         TYPE
         PrintOBJ = object
            constructor Init;
            procedure   PrintChar(Ch:char);
            procedure   PrintStr(Str:string);
            procedure   PrintInt(Int:integer);
            destructor  Done;
         end; {PrintOBJ}

         IMPLEMENTATION
         constructor PrintOBJ.Init;
         {no data to initialize}
         begin

         end; {PrintOBJ.Init}

         procedure PrintOBJ.PrintChar(Ch:char);
         {}
         var
           Lst: text;
         begin
            Assign(Lst,'LPT1');
            Rewrite(Lst);
            Write(Lst,Ch);
            Close(Lst);
         end; {PrintOBJ.PrintChar}

         procedure PrintOBJ.PrintStr(Str:string);
         {}
         var I : integer;
         begin
            for I := 1 to length(Str) do
                PrintChar(Str[I]);
         end; {PrintOBJ.PrintStr}

         procedure PrintOBJ.PrintInt(Int:integer);
         {}
         var I:integer; Str:string;
         begin
            Str := IntToStr(Int);
            for I := 1 to length(Str) do
                PrintChar(Str[I]);
         end; {PrintOBJ.PrintInt}

         destructor PrintOBJ.Done;
         {no data to dispose of}
         begin
         end; {PrintOBJ.Done}

         end.



16-6                                                       Extending the Toolkit

--------------------------------------------------------------------------------

         The two main methods are PrintStr and PrintInt, but these methods both
         call the method PrintChar to print each individual character. (By
         design, the code is simplistic; just remember the main purpose is to
         teach you about extensibility, not how to write to the printer!)

         PrintChar is the root of the problem because it is hard-coded to print
         to LPT1. If you are getting into the OOP groove, and you're from Cali-
         fornia, you might say: "No sweat, DUDE! Create a descendant object and
         replace the PrintChar method. Gnarly!" It's the right idea, but in this
         case it won't work.

         If you create a descendant object, NewPrintOBJ, and replace the Print-
         Char method, the object will inherit the PrintStr and PrintInt methods.
         However, when you call PrintStr or PrintInt, they will in turn call
         THEIR object's version of PrintChar, i.e. PrintOBJ.PrintChar not New-
         PrintOBJ.PrintChar. One solution is to also replace the PrintStr and
         PrintInt methods, but then you have rewritten the entire object without
         taking advantage of inheritance!

         The OOP solution lies in virtual methods. If the PrintChar method in
         PrintOBJ is declared as virtual, Turbo Pascal will manage the situation
         very differently. A method is identified as virtual by adding the key-
         word virtual at the end of the method declaration. The following code
         fragment shows the PrintOBJ object declared with a virtual method:

         PrintOBJ = object
            constructor Init;
            procedure   PrintChar(Ch:char); virtual;
            procedure   PrintStr(Str:string);
            procedure   PrintInt(Int:integer);
            destructor  Done;
         end; {PrintOBJ}

         The virtual keyword instructs Turbo Pascal to late bind the PrintChar
         method. That is, the compiler implements a special linking method which
         ensures that descendant objects, like NewPrintOBJ, can redefine any
         virtual methods. The redefined version of the method will always be
         called even by inherited methods. For example, the following code
         defines a descendant of PrintOBJ (assuming PrintOBJ used the virtual
         keyword):

         NewPrintOBJ = object (PrintOBJ)
            constructor Init;
            procedure   PrintChar(Ch:char); virtual;
            destructor  Done;
         end; {NewPrintOBJ}

         Now if the inherited method NewPrintOBJ.PrintStr is called, Turbo Pas-
         cal is smart enough to ensure that PrintStr calls NewPrintOBJ.Print-
         Char, and not PrintOBJ.PrintChar. All you would have to do is customize
         PrintChar to print to any port and the problem is solved. The on-disk
         demo file, EXTDEM2.PAS, includes the full solution for this example.


OOP Principles                                                              16-7

--------------------------------------------------------------------------------

           Note: if an object includes virtual methods, the object must have
           a constructor and destructor. In addition, if a method is declared
           as virtual, all descendant methods with the same name must also be
           declared virtual, and they must have identical passed parameters.




         The down side of virtual methods is that they will always be linked
         into your EXE program code whether the methods are called or not. If
         this were not so, there would be good justification for making every
         method virtual. As it is, the Toolkit only makes methods virtual if
         they are likely to be accessed in some descendant object. It's a trade-
         off between complete flexibility and program code size.

         You may have heard or read about polymorphism. Polymorphism allows sim-
         ilar objects to execute different code when the same call is made to
         them from some independent routine. It is virtual methods which provide
         Turbo Pascal objects with polymorphism. One of the best Toolkit exam-
         ples of polymorphism is in the totIO units. The FormOBJ object, which
         controls all the input fields, knows that every input field object has
         the methods Display, Select, ProcessKey and Suspend. Any object which
         is created as a descendant from BaseIOOBJ will inherit these methods,
         and can be managed by FormOBJ. All FormOBJ needs to do is call one of
         these methods. (Actually, there are a few more methods than listed, but
         the theory still holds.) In chapter 20: Extending Input Fields the full
         power of polymorphism is explored.



Know Your Ancestor!

         As you have probably assessed, you need to know some details about the
         Toolkit objects before you can start to extend them. You need to know
         about the methods and the data that will be inherited. Its a good idea
         to print the interface section of each unit, as this provides all the
         important information about each object's data and methods.

         This concludes the brief explanation of OOP. Remember that the text is
         aimed at helping you to extend the Toolkit. You should consider re-
         reading the discussion of object-oriented programming in the Turbo Pas-
         cal's User's Guide. It might make a lot more sense now! In addition, it
         covers, in much greater depth, some of the principles introduced in
         this chapter.

