INTRODUCTION TO OBJECT-ORIENTED PROGRAMMING. ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß It is good programming practice to develop sub-routines, procedures or functions for blocks of code which perform well-defined and frequently repeated tasks. The procedure or function may have 'formal' parameters, as declared, so that it can be called with different 'actual' parameters. It is also sensible to bundle together any associated data such as name, address and telephone number, into a record. Each of the associated items of data is stored in a separate 'field' of the record and can be accessed using the syntax ., e.g. Personnel.Name Even though these techniques are employed, it is still possible to use a procedure or function with the wrong data. In many circumstances it is therefore wise to lock together procedures and functions with the associated data. This is object-oriented programming or object-oriented design (OOD) of programs. Object-oriented programming was introduced in the early 70's in the simulation language Simula and then developed in the Smalltalk language. Object-oriented design of software has only recently entered the mainstream of computing. Turbo Pascal, version 5.5, with Object-Oriented Programming (OOP), was introduced in 1989. Object-oriented programming was based on the observation that computer programs perform actions on objects, such as records in databases, lines of text, and graphical shapes. Traditional software was simply a list of actions performed on certain data in a certain sequence. Changing the data or the actions usually meant changing the program, sometimes significantly, and hence disturbing tested code. OOP offers an opportunity to change the way the data is manipulated by extending the program rather than changing it, so that tested code is retained. An 'object' type is an extension of the structured 'record' type. However, it also contains functions and procedures, declared locally within the object type declaration, and these are used to manipulate the data. These functions and procedures are jointly called 'methods'. Although the method and its parameters are declared within the object type declaration, the code for the implementation of the method is defined outside the object, somewhere in the program before that particular method is called. Since the method is declared within the object type declaration, the method is rigidly bound to that object type. This is called Encapsulation. As with records, the type is first declared in the 'type' declaration part of the program. Then specific variables of that type are declared in the 'var' declaration part of the program. This can be illustrated by means of a simple example, which compares the conventional way to initialize a record with the initialization of the same data by means of an object defined with its own method. The two programs are called RECINIT.PAS and OBJINIT.PAS respectively. The type declaration for the object is as follows: type Obj = object { compared with 'Rec = record' } i : integer; r : real; s : string[50]; procedure Init( int : integer; re : real; st : string); { this last line is not in the record type declaration } end; The essential difference is that the procedure 'Init' is declared after the data fields in the object declaration. This Init procedure has ceased to exist as a separate entity and is now an integral part of the 'Obj' object type, although the code for the initialization method is defined separately and referenced by means of the 'dot' notation as below: procedure Obj.Init( int : integer; re : real; st : string); { In the record case, the procedure name would just be 'Init', but the parameters would include the formal 'var DataRec : Rec' } begin i := int; r := re; s := st; end; The program is completed by a 'var' declaration and a main part as follows: var ThisObj : Obj; begin ClrScr; ThisObj.Init(1234, 9.876, 'This is the string entry for this object'); { In the record case, a final actual parameter, a rec-type variable 'ThisRec' would be included } writeln( 'The integer value is ', ThisObj.i); writeln( 'The real value is ', ThisObj.r); writeln( 'The string is " ', ThisObj.s, '"'); end. If more than one variable of type 'obj' is declared, each variable would have its own region of memory reserved for the field entries, but would share a common pointer to the procedure 'Init'. Furthermore, it would be impossible to call this procedure without specifically involving an object of type 'obj'. The procedure is now referred to by the dot notation as 'Obj.Init', which tells the compiler that the procedure is part of the 'Obj' object type. When a variable, or instance, 'ThisObj', of the type 'Obj' is declared, the same dot notation is used - ThisObj.Init(....); Another difference is the absence of the last parameter in the procedure. The formal parameter 'var DataRec : Rec' is not replaced with a parameter referring to the object, since the data fields are already bound to the procedure by the object type declaration. In OOP parlance, a 'class' or 'object type' is an abstraction that provides a template for objects, which are called 'instances' of that class. Thus 'Obj' is a template (type or class) and 'ThisObj' is an instance. Whereas in literary work plagiarism is unacceptable, it is positively encouraged in OOD. Objects are inherited and extended and in many cases there is a natural hierarchy as with graphical objects: location(x,y) point (x,y,visible) circle (x,y,visible,r) arc (x,y,visible,r,start angle,end angle) The data fields for location can be inherited and then extended for the point by the addition of a boolean (visible) field. A circle then inherits these fields and adds the radius field and so on. It is the field structure of the object type, not actual numeric values, that is inherited. Each instance of each object type must be initialized, so that specific numerical values are assigned to each data field. A descendant object type inherits type compatibility with all its ancestor types. This relaxation of Turbo Pascals normal type compatibili`ty rules is explained in more detail in the note COMPAT.TXT which may be consulted later when use is made of this fact. Methods to move a point from (x1,y1) to (x2,y2) can be inherited by a circle and an arc, although the methods to show and hide the different graphical objects will be different. One of the major advantages of object-oriented programming is the ability to build a hierarchy of descendant objects, with each descendant inheriting access to all its ancestors' code and data fields. 'Inheritance' is a major attraction of OOD, but some care has to taken when inherited methods are not relevant to the descendant object as for example for a point 'show' and a circle 'show': procedure Point.Show; begin Visible := True; {boolean field is set 'true' for information} PutPixel(X, Y, GetColor); {pixel at X,Y is 'put' in foreground colour} end; procedure Circle.Show; begin Visible := True; {boolean field for information & inspection} Graph.Circle(X, Y, Radius); {draw circle in current foreground colour} end; In this case the inherited method 'Show' from the object type point must be overridden for the object type circle. This is achieved by using 'Virtual Methods', which is explained in detail in the note VIRTUAL.TXT which should be read next. OOPINTRO.TXT revised 31.5.93