1.03k likes | 1.23k Views
Eiffel: Analysis, Design and Programming Bertrand Meyer (Nadia Polikarpova ). Chair of Software Engineering. - 8 - Inheritance. Overview. Basics Redefinition Polymorphism and dynamic binding Inheritance and contracts Deferred classes Polymorphic containers
E N D
Eiffel: Analysis, Design and Programming Bertrand Meyer (Nadia Polikarpova) Chair ofSoftware Engineering
- 8 - Inheritance
Overview • Basics • Redefinition • Polymorphism and dynamic binding • Inheritance and contracts • Deferred classes • Polymorphic containers • Inheritance and genericity: constrained genericity • Multiple inheritance • Non-conforming inheritance • Covariance and anchored types
BAG_OF_CARS LIST_OF_CITIES LIST_OF_ PERSONS LINKED_LIST_OF_CARS Extending the basic notion of class Inheritance Abstraction Genericity Type parameterization Type parameterization LIST_OF_CARS Specialization
Inheritance basics Principle: Describe a new class as extension or specialization of an existing class (or several withmultipleinheritance) If B inherits from A : Asmodules: all the services ofAare available inB(possibly with a different implementation) Astypes: whenever an instance ofAis required, an instance of Bwill be acceptable(“is-a” relationship)
Terminology IfBinheritsfromA(by listingAin itsinheritclause): Bis anheirofA AisaparentofB For a class A: Thedescendantsof Aare Aitself and (recursively) the descendants of A ’sheirs Proper descendantsexclude A itself Reverse notions: Ancestor Proper ancestor More precise notion of instance: Direct instances of A Instances of A: the direct instances of A and its descendants (Other terminology: subclass, superclass, base class) A B C D E
What you can do to inherited features • Effect (implicit) • Redefine • Undefine • Rename • Change export status • Select
Example hierarchy display* rotate* move* center* * FIGURE * OPEN_FIGURE * CLOSED_FIGURE perimeter* display + rotate + move + perimeter + perimeter + SEGMENT POLYLINE + ELLIPSE + POLYGON ... perimeter ++ side1 side2 diagonal RECTANGLE TRIANGLE ... perimeter ++ CIRCLE display ++ rotate ++ move ++ * deferred + effective ++ redefined display + rotate + move + perimeter ++ SQUARE
Redefinition • Class may redefine (“override”) inherited features • Redefinition is explicit • In line with Uniform Access principle, may redefine inherited function into attribute Not the other way around!
Redefinition 1: polygons classPOLYGONinherit CLOSED_FIGURE create make feature vertex : ARRAY [POINT] vertex_count: INTEGER perimeter : REAL is -- Perimeter length do from...until...loop Result:=Result+ vertex [i]. distance (vertex [i + 1]) ... endend invariant vertex_count >= 3 vertex_count = vertex.count end vertex [i ] vertex [i + 1]
Redefinition 2: rectangles classRECTANGLE inherit POLYGON redefine perimeter end create make feature diagonal, side1, side2 : REAL perimeter : REALis -- Perimeter length doResult:= 2 (side1 + side2)end invariantvertex_count = 4 end diagonal side2 side1
Inheritance, typing and polymorphism • Assume: • p: POLYGON; r: RECTANGLE; t: TRIANGLE • x: REAL • Permitted: • x := p.perimeter • x := r.perimeter • x := r.diagonal • p := r p (POLYGON) r (RECTANGLE) NOT permitted: x := p.diagonal -- Even just after p := r ! r := p
Dynamic binding • What is the effect of the following (if some_test is true)? • ifsome_testthen • p := r • else • p := t • end • x := p.perimeter • Redefinition: A class may change an inherited feature, as with POLYGON redefining perimeter. • Polymorphism: p may have different forms at run-time. • Dynamic binding: Effect ofp.perimeter depends on run-time form of p.
Without dynamic binding? • display (f:FIGURE) • do • if“f is a CIRCLE”then • ... • elseif“f is a POLYGON” then • ... • end • end • and similarly for all other routines! • Tedious; must be changed whenever there’s a new figure type
With inheritance and associated techniques f: FIGURE c: CIRCLE p: POLYGON With: and: • createc.make (...) • createp.make (...) Then just use: Initialize: • f.move (...) • f.rotate (...) • f.display (...) • -- and so on for every-- operation on f! • if ... then • f := c • else • f := p • end
Binding Binding is the process in which A routine invocation f (a), x.f (a) is connected to code that is executed Accessing or writing to a variableaa := Eis connected a memory location.
Static vs. dynamic binding If binding is done at compile time, this is called “Static Binding” using “Static Linking”. If binding is done at program load time, this is called “Static Binding” using “Dynamic Linking”. If binding is done at runtime this is called “Dynamic Binding”.
Static Binding in C In the .h header file: externintfoo (argc); externfloatmy_variable; In the .c file: intfoo (argc); floatmy_variable;
Execution of a call in C On the caller side: Push the arguments on the stack Push the current PC + 1 on the stack (return address) Jump to the code to execute This is “filled out” during linking/binding Clean the stack
Execution of a call in C (cont.) On the callee side: Add some extra memory on the stack for the local variables Initialize the local variables (if necessary) Execute the implementation of the routine Read the return address from the stack Jump to the return address
Dynamic Binding in non-OO Dynamic binding is not exclusive for object-orientation: Lisp (1958) Forth (1970) - a mixture called “Threaded Code” C (1970) - Why ?
Dynamic Binding in C #include <stdio.h> voidhello() { printf ("Hello\n"); } voidworld() { printf ("World\n"); } intmain (intargc, char **argv) { void (*func)(void); if (argc > 1) func = hello; elsefunc = world; func(); /* Dynamic Binding */ }
Dynamic Binding and OO We need dynamic binding in object-orientation Identifiers used in program text are relative to the current object: Attributes are relative to Current Routines may be redefined in subclasses
Using class tables Class Table Pointer Class A Integer Field Pointer to feature f Integer Field Pointer to feature g Integer Field classA feature a,b,c : INTEGER fis do c := a + b g end gis ... end 1. Read first field from Current 2. Add second field from Current 3. Store result in third field of Current 4. Go to the second entry in Current's class table
With inheritance A classB inherit A redefine f feature d: BOOLEAN fis ... his ... end B Class Table Pointer Class B Integer Field Pointer to feature f Integer Field Pointer to feature g Integer Field Pointer to feature h Boolean Field
Contracts and inheritance • Issue: what happens, under inheritance, to • Class invariants? • Routine preconditions and postconditions?
Invariants • Invariant Inheritance rule: • The invariant of a class automatically includes the invariant clauses from all its parents, “and”-ed. • Accumulated result visible in flat and interface forms.
D A C B Contracts and inheritance r require a1: A … a1.r (…) ensure Correct call in C: ifa1.then a1.r (...) -- Herea1.holds end r ++ r require ensure client of inherits from ++ redefinition
Assertion redeclaration rule • When redeclaring a routine, we may only: • Keep or weaken the precondition • Keep or strengthen the postcondition
Assertion redeclaration rule in Eiffel • A simple language rule does the trick! • Redefined version may have nothing (assertions kept by default), or • require elsenew_pre • ensure thennew_post • Resulting assertions are: • original_preconditionornew_pre • original_postconditionandnew_post
The role of deferred classes • Express abstract concepts independently of implementation • Express common elements of various implementations • Terminology: Effective = non-deferred • (i.e. fully implemented)
A deferred feature • In e.g. LIST: • forth • deferred • end require notafter ensure index = oldindex + 1
Mixing deferred and effective features • In the same class • search (x: G) • -- Move to first position after current-- wherexappears, orafterif none. • do • fromuntilafterorelseitem= xloop • forth • end • end • “Programs with holes” Effective! Deferred!
“Don’t call us, we’ll call you!” • A powerful form of reuse: • The reusable element defines a general scheme • Specific cases fill in the holes in that scheme • Combine reuse with adaptation
Deferred classes in EiffelBase * CONTAINER * * * BOX COLLECTION TRAVERSABLE * * * * * * INFINITE BAG SET HIERARCHICAL LINEAR FINITE * * * * * * * INTEGER_ INTERVAL UNBOUNDED COUNTABLE TABLE ACTIVE BILINEAR BOUNDED … … * * * * * RESIZABLE CURSOR_ STRUCTURE INDEXABLE DISPENSER SEQUENCE * * ARRAY STRING HASH_TABLE STACK QUEUE * deferred
deferred classVAT inherit TANK feature in_valve, out_valve: VALVE fillis-- Fill the vat. requirein_valve.open out_valve.closed deferredensurein_valve.closed out_valve.closed is_full end empty, is_full, is_empty, gauge, maximum, ... [Other features] ... invariant is_full = (gauge >= 0.97 * maximum) and(gauge <= 1.03 * maximum) end Deferred classes for analysis
Polymorphic data structures classLIST [G] feature ... last: Gis... extend (x: G) is... end fl: LIST [FIGURE] r: RECTANGLE s: SQUARE t: TRIANGLE p: POLYGON ... fl.extend (p); fl.extend (t); fl.extend (s); fl.extend (r) fromfl.startuntilfl.afterloopfl.item.display; fl.forthend fl (SQUARE) (TRIANGLE) (RECTANGLE) (POLYGON)
Figure hierarchy (reminder) display* rotate* move* center* * FIGURE * OPEN_FIGURE * CLOSED_FIGURE perimeter* display + rotate + move + perimeter + perimeter + SEGMENT POLYLINE + ELLIPSE + POLYGON ... perimeter ++ side1 side2 diagonal RECTANGLE TRIANGLE ... perimeter ++ CIRCLE display ++ rotate ++ move ++ * deferred + effective ++ redefined display + rotate + move + perimeter ++ SQUARE
Enforcing a type: the problem • fl.store ("FILE_NAME") • ... • -- Two years later: • fl := retrieved ("FILE_NAME“) • x:= fl.last-- [1] • print (x.diagonal)-- [2] • What’s wrong with this? • If x is declared of type RECTANGLE, [1] is invalid. • If x is declared of type FIGURE, [2] is invalid.
Enforcing a type: the Object Test Optional (if omitted just tests for void) Expression to be tested Object-Test Local • if attached{RECTANGLE} fl.lastasrthenprint (r.diagonal) • … Do anything else with r, guaranteed to be non-void • … and of dynamic type (descendant of) RECTANGLE • elseprint ("Too bad.") • end SCOPEof the Object-Test Local
Earlier mechanism: assignment attempt • f: FIGURE • r: RECTANGLE • ... • fl.retrieve ("FILE_NAME") • f := fl.last • r?=f • ifr /= Void thenprint (r.diagonal)elseprint ("Too bad.")end
Assignment attempt • x ?= y • with • x: A • Semantics: • Ifyis attached to an object whose type conforms to A, perform normal reference assignment. • Otherwise, make xvoid.
What we have seen • Basics • Redefinition • Polymorphism and dynamic binding • Inheritance and contracts • Deferred classes • Polymorphic containers
Topics for today • Inheritance and genericity: constrained genericity • Multiple inheritance • Non-conforming inheritance • Covariance and anchored types
Inheritance + Genericity Unconstrained genericity LIST[G] e.g.LIST [INTEGER], LIST [PERSON] Constrainedgenericity HASH_TABLE[G, H―> HASHABLE ] VECTOR [G―> NUMERIC ]
Constrained genericity • class VECTOR[G ] feature • plusalias"+" (other: VECTOR [G]): VECTOR [G] • -- Sum of current vector andother. • require • lower = other.lower • upper = other.upper • local • a,b,c:G • do • ... See next ... • end • ... Other features ... • end
= i c a b + Adding two vectors u v = w + 2 1
Constrained genericity • Body of plusalias "+": • createResult.make (lower, upper) • from • i := lower • until • i > upper • loop • a := item(i) • b := other.item(i) • c := a + b-- Requires “+” operation on G! • Result.put (c, i) • i := i + 1 • end
The solution • Declare class VECTORas • classVECTOR[G –>NUMERIC] feature ... The rest as before ... • end • Class NUMERIC (from the Kernel Library) provides features plusalias"+", minusalias"-"and so on.
Improving the solution • Make VECTORitself a descendant of NUMERIC, • effecting the corresponding features: • classVECTOR[G –> NUMERIC] inherit • NUMERIC • feature • ... Rest as before, includinginfix"+"... • end • Then it is possible to define • v : VECTOR [INTEGER] • vv : VECTOR [VECTOR [INTEGER]] • vvv : VECTOR [VECTOR [VECTOR [INTEGER]]]