home *** CD-ROM | disk | FTP | other *** search
- 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.
-