500 likes | 562 Vues
Object Oriented Programming and Object Oriented Design. Programming Languages Robert Dewar. Object Oriented Programming. OOP provides three fundamental functionalities: Type extension Inheritance Dynamic Polymorphism. Type Extension.
E N D
Object Oriented ProgrammingandObject Oriented Design Programming Languages Robert Dewar
Object Oriented Programming • OOP provides three fundamental functionalities: • Type extension • Inheritance • Dynamic Polymorphism
Type Extension • The problem, given an existing type, add additional capabilities retaining the original • Make the extended type look as much like the original as possible • Typical implementation, add fields or components to an existing record type.
Faking Type Extension • Could make a new record • With new components • And retaining the old type as one component • type Ext is record Parent : Old; Newf : Integer;end record; • But that would not look much like original • If Newobj is of type Ext • Cannot say Ext.Value • Must say Ext.Parent.Value
Inheritance • Goes along with type extension • When a type is extended • New type has all the operations of the old type, with the same meaning. • Of course they do not know about the new fields, and do not reference them • But most code that worked for the base type works for the extended type without change
More on Inheritance • Cannot always use operations of base type • May need to mess with new fields • In particular constructors must do so • May have different semantics from new field • Should be able to “override” inherited operations when type is extended • Must override constructors • Can also add new operations for new extended type
Faking Inheritance • If you fake type extension • You could add definitions for every operation. Most would look like • procedure Operate (X : Ext) isbegin Operate (X.Parent);end Operate; • That’s rather annoying • And generates a lot of junk
Ad Hoc Polymorphism • More familiar term is overloading • Applies in this situation as follows • If we have several types derived from a common parent with an operation Op • Suppose Op is overridden for some types • If a variable has a particular type, then the compiler can figure out what Op you mean from the type of the variable
Dynamic Polymorphism • Also called dynamic dispatching • Addresses problems where you have data structures that are heterogenous and can contain different kinds of data. • The data items are similar (e.g. obtained by type extension from a common base). • And therefore have a similar set of operations.
More on Dynamic Dispatching • Now suppose we apply Op to a variable which at run time can have more than one possible “type”. • What we want is that at runtime, the proper Op is picked, based on the current type of the object in the variable • This is called dynamic dispatching
Faking Dynamic Dispatching • We could have record fields that contained pointers to the function to be called. • Actually that’s how dynamic dispatching is usually implemented: • Record contains a pointer to a table • Table has (at fixed offsets for any given operation), address of function to be called. • Different types have different tables
An Approach to Compare • The issue is that we want a variable that can have several different forms at runtime. • And we have some code that depends on which particular form it has. • And some code that is the same for all types.
Using Variant Records • Instead of N types extended from a given base type, use a variant record with N different possibilities. • Fields of parent correspond to common fields in the variant record • Code that does not depend on type just accesses these common fields
Using Variant Records (cont) • Code that is not common has a case statement: • case Object.DiscrimValue is when val1 => …when val2 => … …end case;
Comparing the Approaches • Consider that you have N operations and T types, then potentially you have N*T different functions, but in practice many of the functions are the same for many types. • Case statement means you have one unit per operation, using case to select • Dynamic dispatching means you have one unit per type, with overridden operations.
Comparing the Approaches (cont.) • If you often add operations, the case statement approach is easier, add one new unit for new operation providing base code with case statements as required • If you often add types, the dynamic dispatching approach is easier, add one new unit for new type overriding any operations where base code is wrong.
OOP and Reuse • By making your types extendible • You increase reuse capabilities • Instead of editing your code in places where it does not apply • A client extends your types, and overrides your code where it does not apply
Object Oriented Design • Has nothing to do with OOP per se • Relates not to language features but to the design approach • It may be that OOP features are useful for object oriented design (OOD).
OOD – The Basic Idea • The problem is modeled as a set of objects, preferably related to the structure of the problem, that represent real objects in the world. These objects have state. • Computation proceeds by passing messages (requests, signals, commands, reports) between objects.
How does OOD relate to OOP • In the real world, objects are built by specializing more general notions • A Toyota Previa is an instance of Car with extra info, which is an instance of Vehicle with extra information, etc. • Type extension • All cars work mostly the same • Inheritance • But for some features, cars differ • Dynamic dispatching
OOP Features in Ada 83 • Ada 83 provides features for • Inheritance • But does not provide • Type extension • Dynamic dispatching • Inheritance is provided via derived types • Other OOP features deliberately omitted • Designers were very familiar with Simula-67 • But felt that genericity was a better approach
Derived Types • Declare a type and some operations on it • type Base is ….procedure Print (Arg : Base);function New_Base return Base; • Now derive a new type • type Mybase is new Base; • All operations are available on Mybase Including for example Print and New_Base • But you can redefine (override) any inherited operations.
OOP In Ada 95 • Genericity is not enough • Market demands OOP features • So in Ada 95 features are added for • Type Extension • Dynamic Dispatching • But multiple inheritance is deliberately omitted
Tagged Types in Ada 95 • A tagged type has a dynamic tag showing what type the object is. Otherwise it looks like a record • type Base is tagged record X : Integer; Y : Float;end record; • Can also have tagged private types • type Base is tagged private; • Completion must be tagged record
Type Extension • A tagged type can be extended • Using an extension of derived type idea • type Mybase is new Base with record B : Boolean; D : Duration;end record; • All operations are inherited • Except for constructors (functions returning values of type Base) • Constructors must be overridden • Since they need to know about the new fields
How type Extension Works • New fields are added at the end of the record, with original fields at the start. • A subprogram that is only referencing the original fields can do this on the base type or any type derived from it. • Because the original fields are always at the same offset from the start of the record. • This model does not extend well to the case of multiple inheritance.
Type extension and overloading • Suppose a client has • B : Base; • M : Mybase; • And there is an operation D that was not overridden: • D (B); D (M); • Correct proc called, but in fact does same thing • And there was an overridden operation O • O (B); O (M); • Correct proc called (static overloading)
Converting Among Types • Suppose we have an operation Q that is defined for Base and was not inherited • Because it was not defined in original package • And now we have a Mybase: • M : Mybase; • And we want to call Q on M: • Q (M); -- no good, wrong type • Q (Base (M)) -- that’s ok, a view conversion
Using conversion when Overriding • Suppose we have a procedure Dump defined on Base • For Mybase we want to dump the new fields and then call the original dump • procedure Dump (X : Mybase) isbegin … dump new fields Dump (Base (X)); -- calls original Dumpend Dump;
Converting the Other Way: NOT • Suppose we have an operation M that is defined for Mybase, and we have an object of type Base: • B : Base; • And we want to apply M to B • You are out of luck, can’t do it • After all M might refer to extended fields!
Class Variables • Suppose you want a data structure that holds a mixture of objects of type Base and Mybase. • The type Base’Class is a type that includes values of tagged type Base and all types derived from Base. • type Bptr is access Base’Class; • BC_Ptr : Bptr := new Base’(….);BC_Ptr := new Mybase’(….);
Dynamic Dispatching • If a subprogram, say Draw is defined as a primitive operation of type Base (defined along with type Base) • Then not only is it inherited by any type derived from Base • But it is also defined on Base’Class
Special Treatment of Base’Class • The subprogram • procedure Draw (Arg : Base’Class); • That is derived automatically • Has special semantics • It is called with an object of the appropriate type (e.g. BC_Ptr.all) • The result is to call the version of Draw that is appropriate to the actual run-time type of the argument (looks at the tag)
How Dynamic Dispatching Works • Tag is actually a pointer to a table • One table for each type • In our example, two tables • One table for Base • Different table for Mybase • Table contains pointers to subprograms • Put new ones at end • First entries in Mybase table are a copy of the entries in the Base table unless overridden.
Object-Oriented programming in C++ • Classes as units of encapsulation • Information Hiding • Inheritance • polymorphism and dynamic dispatching • Storage management • multiple inheritance
Classes • Encapsulation of type and related operations class point { double x,y; // private data members public: point (int x0, int y0); // public methods point () { x = 0; y = 0;}; // a constructor void move (int dx, int dy); void rotate (double alpha); int distance (point p); }
A class is a type : objects are instances point p1 (10, 20); // call constructor with given arguments point p2; // call default constructor Methods are functions with an implicit argument p1.move (1, -1); // special syntax to indicate object // in other languages might write move (p1, 1, -1) // special syntax inspired by message-passing metaphor: // objects are autonomous entities that exchange messages.
Implementing methods No equivalent of a body: each method can be defined separately void point::rotate (double alpha) { x = x * cos (alpha) - y * sin (alpha); y = y * cos (alpha) + x * cos (alpha); }; // x and y are the data members of the object on which the // method is being called. // if method is defined in class declaration, it is inlined.
Constructors • One of the best innovations of C++ • special method (s) invoked automatically when an object of the class is declared point (int x1, int x2); point (); point (double alpha; double r); point p1 (10,10), p2; p3 (pi / 4, 2.5); • Name of method is name of class • Declaration has no return type.
The target of an operation • The implicit parameter in a method call can be retrieved through this: class Collection { Collection& insert (thing x) { // return reference … modify data structure return *this; // to modified object }; }; my_collection.insert (x1).insert (x2);
Static members • Need to have computable attributes for class itself, independent of specific object; e.g. number of objects created. • Static qualifier indicates that entity is unique for the class staticint num_objects = 0; point () { num_objects++;}; // ditto for other constructors • Can access static data using class name or object name: if (point.num_objects != p1.num_objects) error ();
Classes and private types • If all data members are private, class is identical to a private type: visible methods, including assignment. • A struct is a class with all public members • How much to reveal is up to programmer • define functions to retrieve (not modify) private data int xcoord () { return x;}; int ycoord () { return y;}; p2.x = 15; // error, data member x is private
Destructors • If constructor allocates dynamic storage, need to reclaim it class stack { int* contents; int sz; public: stack (int size) { contents = newint [ sz = size];}; void push (); int pop (); int size () { return sz;}; } stack my_stack (100); // allocate storage dynamically // when is my_stack.contents released?
If constructor uses resources, class needs a destructor • User cannot deallocate data because data member is private: system must do it ~stack ( ) {delete[ ] contents;}; • inventive syntax: negation of constructor • Called automatically when object goes out of scope • Almost never called explicitly
Copy and assignment point p3 (10,20); point p5 = p3; // componentwise copy • This can lead to unwanted sharing: stack stack1 (200); stack stack2 = stack1; // stack1.contents shared stack2.push (15); // stack1 is modified • Need to redefine assignment and copy
Copy constructor stack (const stack& s) { // reference to existing object contents = new int [ sz = s.size()]; for (int I = 0; I <sz; I++) contents [I] = s.contents [I]; } stack s1 (100); … stack s2 = s1; // invokes copy constructor
Redefining assignment • Assignment can also be redefined to avoid unwanted sharing • Operator returns a reference, so it can be used efficiently in chained assignments: one = two = three; stack & operator= (const stack& s) { if (this != &s) { // beware of self-assignment delete [] contents; // discard old value contents = newint [sz = s.size ()]; for (int I = 0; I <sz; I++) contents [I] = s.contents [I]; } return *this; } stack s1 (100), s2 (200); … s1 = s2; // transfer contents
Differences Between Ada and C++ • C++ model much more specialized to the notion of OOD • Distinguished first parameter is object involved • No easy way of defining binary operators • Prefix notation nice for objects but awkward for values • C++ allows multiple inheritance
Doing Multiple Inheritance in Ada • We can have one field that we add be an instance of some other base type. • We can use generics to parametrize this additional type • Worked out examples in Ada 95 Rationale • Which you can find at www.adapower.com
OOP in Java • To be supplied!