520 likes | 709 Views
Object Oriented Programming. Programming Languages 1 Robert Dewar Fall 2003. Object Oriented Design. This term refers to a design principle (not to any particular prog lang features) In OOD, the problem is modeled as interaction between objects
E N D
Object Oriented Programming Programming Languages 1 Robert Dewar Fall 2003
Object Oriented Design • This term refers to a design principle (not to any particular prog lang features) • In OOD, the problem is modeled as interaction between objects • The computation focuses on the objects rather than on procedural computations
What is an Object? • An object is a structure that may (and usually does, hence the name) have internal state. • It is associated with “methods” that are prepared to accept messages from other objects. • The computation is modeled in terms of message passing between objects.
Simula-67 • This was the first object oriented language. As you can tell from the name it is old (40 years old!) • So this is not a new idea! • In Simula-67, each object is a separate thread of control • From a PL point of view, each object is a task or thread, and message passing involves synchronization between threads.
More on Simula-67 • The idea behind the language is that in real life, systems consist of independent objects (machines, people, reactive systems etc) • These objects then interact with one another. • The language allows modeling of these objects to simulate the real life system
More on OOD • OOD is possible in any language • Objects can be modeled using available datatypes • Along with specific sets of procedures or functions that correspond to the methods • The association of methods with objects in a non-OO language is one of convention and organization
OOD Methodology • Object Oriented Design is all about finding the objects in your problem space • Then representing them as objects in your program • Then modeling the system by interactions between these objects using message passing. • Various formal methodologies e.g. HOOD allow OOD to be independent of actual program.
Pure Object Oriented Design • Absolutely everything is an object • There is no data other than objects • Even an integer is an object • “hello integer object, I have a message to send you that contains an integer value. The value is 3. Could you please add this to yourself and report back.” • “No problem, I did as you asked, and the result is 5”
So What is Relation of OOD with OOP (Object Oriented Programming)? • Less than you think! • Let’s go back further here • We will start with a structure (or record) • This can be used to hold arbitrary heterogenous data. • We might think of this as an object, but that’s not necessarily helpful if we don’t adopt an OOP view of the world.
Abstract Datatypes • If we take the record, and associate a number of procedures and functions with the record type • And then we declare that access to variables of this type (we hesitate to use the word objects which is overloaded here: • Object, an object in the OOD sense • Object, a variable/constant of some type
More on ADT’s • The only access allowed is via the procedures and functions. • We access or change data within the ADT by calling one of these procedures or functions passing a variable of the appropriate type as an argument. • So far this does not have anything directly to do with OOD
ADT Support in Languages • We can do ADT’s in any language by adopting conventions • But pleasant languages have specific support for this notion • A way of associating the functions and procedures with a particular type • A way of hiding away the data so that it cannot be accessed
ADT’s in Ada and C++ • In Ada • The package is the mechanism for associating procedures and functions with a type. More accurately, association in a declarative region. • Access is restricted by use of private types • In C++ • The class is the mechanism for associating procedures and functions with a type. • Private members provide privacy
ADT’s and Code Reuse • Reusing code is a good thing • Suppose we have programmed some useful ADT • Now we want to use that ADT directly, great, we can use it as is, unchanged • But suppose we want to use a slightly modified version, then we have to mess with the sources, not good
An Example of Reuse • Binary tree ADT • Handles adding new nodes • Allocating and deleting nodes • Traversing the tree • But what I need is an AVL tree • Which is a binary tree with • An extra flag on each node for balance • Balancing routines
The Goal: ReuseWithout Source Modification • Achieved using four separate but related concepts • Type extension: adding new fields to an existing data structure (type) • Inheritance: when new fields are added, old operations still work on modified type • Overriding: allowing redefinition of operation • Dynamic dispatching: Same operation does different thing on different modified versions, and we choose right operation at run-time.
Type Extension • We have a record type • type T is record …. end record; • And now we want to add a field to this record • We can either do this manually • Or we can have the Prog Language help
Manual Type Extension • type Node;type Node_Ptr is access Node;type Node is record Lson, Rson : Node_Ptr; Value : Integer;end record; • type AVL_Node is record Parent : Node; Balance : Balance_Type;end record; • Now all those operating on AVL_Node can access the parent field and apply operations of the parent type.
Type Extension in the PL • Example is Ada (but langs are similar!) • type Node is tagged record Lson, Rson : Node_Ptr;end record; • Here tagged indicates that the type can be extended (we will see why it is called tagged in a moment). And to extend it: • type AVL_Node is new Node with record Balance : Balance_Type;end record;
Type Extension in the PL • Now objects of type AVL_Node can be treated as also being of type Node • The trick is to model the parent structure of the previous slide so that the parent is first in the record.
Inheritance • We want basic operations defined on the parent type to be available for the child type. • Again two ways of doing things • Manual (goes along with manual type extension) • Have the PL help (goes along with PL helping with extension)
Manual Inheritance • We just reference the parent field • Suppose Left is defined on Node • function Left (N : Node) return Node_Ptr; • Now if we have an AVL_Node: • function to_Anode_Ptr is new Unchecked Conversion (Node_Ptr, Anode_Ptr);…Anode : AVL_Node;…X := To_Anode_Ptr (Left (Anode.Parent));
Inheritance built in to Language • We extend the type as before: • type AVL_Node is new Node with record Balance : Balance_Type;end record; • Now the operations apply directly, so we automatically have a Left defined on AVL_Node: • Anode : AVL_Node;…X := Left (Anode); • Automatically applies Left to the parent. So an AVL_Node “is a” Node (just a rather special one)
Overriding • This allows us to inherit some operations • But in other cases we want special versions • For example • Procedure Dump_Node (N : Node); • It makes no sense use the same code to try to dump an AVL_Node since it would only dump the old fields, and not the Balance
Manual Overriding • Just declare a new procedure • function Dump_AVL_Node (N : AVL_Node) isbegin Dump_Node (N.Parent); Print (N.Balance); end Dump_AVL_Node; • And by convention always use Dump_AVL_Node when dealing with AVL_Nodes.
Overriding Built Into Language • When we extend type, we get a Dump_Node by automatic inheritance • But we don’t want that so we override: • procedure Dump_Node (N : AVL_Node) is Dump_Node (Node (N)); Print (N.Balance);end Dump_Node; • Now any use of Dump_Node on AVL_Node will use the overriding subprogram.
When to Override • When we want a different behavior (the Dump_Node procedure of the previous slide is an example of this). • When the original operation cannot be used. Notably in the function case: • function Empty_Node return Node; • This code returns a Node, so it cannot return an AVL_Node • Overriding mandatory in this case
Dynamic Dispatching • When we extend type T1 to make type T2, the general term we use is subtyping, so that we say T2 is a subtype of T1. • This is EXACTLY wrong terminology in Ada, here we say T2 is a derived type of T1, and subtypes are something else • In either case, the deriving operation creates a family of related types
Related Types • The types are related both • Conceptually. Remember that we said that an AVL_Node “is a” node, so we are talking about the family of various kinds of nodes. • Physically, the parent is at the start, so all objects in the family start with the same layout (of the original ancestor). • We get a family of types rooted at a particular type (a type and its descendents) which share this relation.
Handling Families of Objects • Suppose we have a bunch of objects of various different types within the same family, e.g. Node, AVL_Node, Red_Black_Node, Node_With_Extra_Field, AVL_Node_With_Count etc. • Supose we deal with pointers • Someone receiving a pointer could treat it as always being a Node
Applying Operations to a Family • Given P, a pointer to a particular element of the node family, we can treat it as a Node. • And we can apply e.g. Lson, Rson and it will work with any possible element of the family, since e.g. an AVL_Node starts with a node. • We are nearly there …
Dispatching Manually • Given AP, a pointer to an AVL_Node, do a manual forced conversion: • function To_P is new Unchecked_Conversion (AVL_Node_Ptr, Node_Ptr);…X := Left (To_P (AP).Parent); This works because the pointer to the AVL_Node also points to a Node structure.
Dispatching Automatically • (but not quite dynamically yet ) • We have the type Node’Class which can reference any element of the family. • Now we can apply Left directly • type NC_Ptr is access Node’Class;…NCP : NC_Ptr;…X := Left (NCP.all); • The function Left on Node’Class is defined automatically, and works on any element of Node.
Finally Getting to Dynamic Dispatching • This is not right for Dump_Node • We really want to apply Dump_Node to an element of the class, and automatically have the right Version of Dump_Node applied. • This is called dynamic dispatching, since we have to dynamically determine which version of Node’s we have and choose the right Dump_Node.
Automatic Dynamic Dispatch • We have the type Node’Class which can reference any element of the family. • Now we can apply Dump_Node directly • type NC_Ptr is access Node’Class;…NCP : NC_Ptr;…Dump_Node (NCP.all); • The function Dump_Node on_Node Node’Class is defined automatically, and works on any element of Node • AND AUTOMATICALLY GETS THE RIGHT DUMP_NODE!
How Did That Work??? • The mechanism here is tricky (and will finally explain the “tagged” in the Ada definition of an extensible type). • The “tag” is an actual field in the record. • Every node object has a tag • The tag is a pointer to a table • The table contains a pointer with one entry per associated subprogram
More on the Dispatch Table (VTable) • The table (Vtable) has one entry for each associated subprogram. • (Vtable is the normal terminology in C++) • The table entry is a pointer to the right version. • Calling a routine consists of looking up the entry in the table and then indirectly calling that routine.
Building the Dispatch Table • All objects of a given type point to the same dispatch table. • Dispatch table is build when type is declared. • When type is extended, extended type gets dispatch table of parent, extended by any new associated subprograms • Entries in this table are overwritten if corresponding operations are overridden
Can we Do Dynamic Dispatching Manually? • Sure, just make the Vtable and explicit type • Make the pointer to it explicit • Replace a call with the steps to extract the right entry and make an indirect call • That’s a pain! • Which is why we like OOP features built into the language
Wait A Moment, Did you say OOP? • Yes, these four language features are collectively called Object Oriented Programming Features • But we didn’t mention objects when discussing them • That’s right, they are generally applicable to a wide range of ADT’s • So why are they called OOP features?
Object Oriented Programming • One of the things that these “OOP” features are useful for is in reusing definitions of objects • The state of an object is represented by an abstract data type. • The notion of a set of associated subprograms maps naturally to a set of methods for the object.
Doing OOD using OOP Features • Each associated subprogram is a method of the object • So it must have as a parameter the object to which it refers. • For example, in Ada • type Obj is ……procedure Update (This : Obj; Val : Integer);…Myobj : Obj…Update (Myobj, 23);
If we Really Like OOD • We can go a bit further • Since all methods will have a This parameter, let’s make it implicit (no need to keep declaring it in each function). • But we still need to say which object • Since the method belongs to an object, think of the method as part of the object.
The C++ Style • A class encapsulates an object, its data fields, and the associated methods. • The methods implicitly get a this parameter and can just refer to elements of the current object • To make the call, we use prefix notation • Obj.Update (3);
Going Further • If we really want to go further, we should insist that the ONLY functions and procedures are methods for objects • That’s where Java goes (C++ still allowed unattached functions). • Of course you can still have a Nothing object which has real procedures, but the emphasis becomes oriented to OOD.
Multiple Inheritance • So far we have been using OOP features for single inheritance • Model is clean and nice • But what if you have a class persistent that provides persistence and a class tree that provides trees, and you want a persistent tree. • No problem, just derive from both classes
No Problem? • Well, first of all, we have conceptual problems. Consider • Start with type A • Derive types A1 and A2 from A • Now derive type B from A1 and A2 • Do we have two A’s around, or only one • Sometimes we want one, sometimes the other, and almost always this causes confusion.
No Problem? (part 2) • Second, it causes implementation difficulties. • The wonderful trick for the single inheritance case was that if type A is derived from type B, directly or indirectly, it has an A at the start, allowing uniform treatment. • But this model does not extend to more than one parent
Multiple Inheritance • C++ implements multiple inheritance • Some kludgy semantics to deal with the confusing duplication case • Some kludge implementation to deal with the implementation issues (which incidentally causes distributed overhead in the non-MI case, basically you carry around an offset to the parent field which is always zero for single inheritance)
Most OO Languages Avoid MI • How can we get effect of MI with no MI: • Two answers • Generic mixins. Suppose our node example instead of being nodes of integers is nodes of type T, then when we instantiate type T, the nodes have the operations of type T available. • Interfaces. If an operation requires only Compare/Move, then we can allow it to be used on any class which has these methods (rather than requiring those classes to be derived from a common Comparable class). So introduce the notion of Comparable Interface.