430 likes | 539 Views
Inheritance and Design by Contract & Genericity. Parents Invariant Rule. The invariants of all the parents of a class apply to the class itself The parent’s invariants are AND’ed together, along with the invariants of this class If no invariants are given then TRUE is assumed
E N D
Inheritance and Design by Contract & Genericity
Parents Invariant Rule • The invariants of all the parents of a class apply to the class itself • The parent’s invariants are AND’ed together, along with the invariants of this class • If no invariants are given then TRUE is assumed • Flat and flat short forms provide a convenient way to see the whole story • Flat is used by the supplier • Flat short is used by the client • Does not have class history – redefine, rename, etc.
C A Meaning of Design by Contract r is require ... ensure end Verify preconditions if not clear they are satisfied -- In C a1 : A if a1.then a1.r check a1. ... assume a1.is true end Verify postconditions. Not needed with exception handling
C A B Enter Dynamic Binding r is require ... ensure end ++ r is require ... ensure end -- In C a1 : A a1 := instance of type B if a1.?pre?then a1.r check a1.?post? ... assume a1.?post?is true end What are ?pre? and ?post? What restrictions are on and ?
How to cheat • Two ways • C expects is sufficient but B has stronger preconditions • don't accept all inputs • demand more from client • client is wrong • C expects is delivered but B has weaker postcondition • deliver outside the range • effectively deliver less -- In C a1 : A a1 := instance of type B if a1.?pre?then a1.r check a1.?post? ... assume a1.?post? end
Be Honest • Replace precondition with a weaker precondition • Expect less from the client than they are prepared to do • require clause becomes weaker • Replace postcondition with a stronger postcondition • Deliver more to the client than they expect to get • ensure clause becomes stronger • Willing to do the job as good as or better
Design by Contract with Dynamic Binding • Contracts cannot be broken by redefinition • Assertions require and ensure are inherited • Every behaviour of the redefined method satisfies the original contract • But can do more • Accept more input cases • Deliver more specific outputs
Subcontracting • Redefinition is like subcontracting • To validate a subcontract requires a theorem prover for the general case • This is inefficient so we provide an approximation or • Weaker precondition is to accept or and • Stronger postcondition is to accept and
Subcontracting – 2 • Language support • When redefining do not use require and ensure • Use require else is or'ed with – the inherited precondition • Use ensure then is and'ed with – the inherited postcondition
Subcontracting example Original definition invert (epsilon : REAL ) is -- Invert matrix with precision epsilon require epsilon >= 10^(– 6) ... ensure abs ((Current * inverse ) – Identity ) <= epsilon end Redefinition invert (epsilon : REAL ) is -- Invert matrix with precision epsilon require else epsilon >= 10^(– 20) ... ensure then abs ((Current * inverse ) – Identity ) <= ( epsilon / 2 ) end
Apparent Precondition Strengthening • Consider the case of general containers that have no bounds on capacity List implementation • Inherit from List but have a bounded capacity container Array implementation • It looks like original has no restrictions when using add but refinement has restrictions • cannot add when full
Apparent Precondition Strengthening – 2 • Actually have the following in the unbounded container require not full • With full defined as returning false • In child define full : BOOLEAN is Result := (count = Capacity ) end • In client have • if not container.full then container.add(...) end • No changes and no surprises in the client • Use abstract preconditions
Assertion Redeclaration Rule • In the redeclared version of a routine it is not permitted to use a require or an ensure clause. Instead you may: • Use a clause introduced by require else to be or'ed with the original precondition • Use a clause introduced by ensure then to be and'ed with the original postcondition • In the absence of such a clause the original is retained • The lazy evaluation (non-strict) form of or else and and then are used
Redefining a function into an attribute • Small problem here • Precondition becomes the weaker True as the value can be accessed at any time • But attributes do not have a postcondition • The postcondition is added to the class invariant • Thereby ensuring the contract still holds foo : INTEGER ... invariant foo = k + 1 end foo : INTEGER is require xyz > 0 ... ensure Result = k + 1 end
On Style • Functions without arguments could be attributes • Could have postcondition or use class invariants • class invariants are the preferred style
Constrained Genericity • Used when the generic type parameters must satisfy some conditions • The following makes sense only if G has thefeature ≥ class RHINO [ G –> COMPARABLE] feature ... minimum ( x , y : G ) : G is do if x ≥ y then Result := y else Result := x end ... end
Constrained Genericity – 2 • In general use the following syntax for constrained genericity • class NAME [ TYPE –> CONSTRAINING_TYPE , ... ] • DICTIONARY [ G , H –> HASHABLE ] • The –> indicates inheritance • H must be a type that inherits from HASHABLE • Inheritance guarantees the type passed has all the features one needs in the context of its use • Unconstrained genericity is really written as follows • STACK [ G –> ANY ]
Type Redeclaration Rule • A redeclaration of a feature may replace the type of the feature (in an attribute or function) or the type of a formal argument (if a routine) by any type that conforms to the original • See Redefining a Signature slides in the set on Inheritance and Adaptation • While the rule guarantees proper typing inconsistencies can arise if types are not changed consistently • Leads to use of Anchored Declarations • The ability to define types relatively and not absolutely
Anchored Declaration • Provide a shortcut for certain kinds of signature redefinitions • Declarations can be made relative to an anchor type rather than providing an absolute declaration class NODE [ G ] creation make feature { NONE } item : G -- what's held in the node next : like Current feature { ANY } make (g : G ) ... change_item ( g : G ) change_next ( other : like next ) end -- NODE Current is the anchor. next points to a node of the same type as Current other is same type as Next – recursive to Current
Anchored Declaration – Rules • The base class of like anchor is • the base class of the type of anchor in the current class • If anchor is Current, then the base class is the enclosing class • Can have recursive definition • like anchor can be based on an anchored type • Do not have cycles in the anchor chain – no knots • While like anchor conforms to its base class T, T does not conform to like anchor • Problems occur if the anchor is redeclared in a subclass (see p603)
Information Hiding & Inheritance • Inheritance and Information Hiding are orthogonal mechanisms • If B inherits from A • B is free to export or hide any feature it inherits in all possible combinations • Need an export clause to change the export status from that of the parent class B inherit A export { NONE } f end -- f is secret export { ANY } g end -- g is public export { X, Y } h end -- h is selectively public ... -- to X, Y and their descendants end
Interface & Implementation Use Inheritance Client Use through interface Information hiding Protection against changes in original implementation Use of implementation No information hiding No protection against changes in original implementation
Multiple & Repeated Inheritance
Multiple Inheritance – Example • Combining two abstractions into one • COMPARABLE and NUMERIC are both useful abstractions • Some abstractions make use of both while others do not NUMERIC COMPARABLE INTEGER COMPLEX STRING
Repeated Inheritance – Example • Ancestor used in multiple paths to descendant UNIVERSITY_ PERSON STUDENT TEACHER TEACHING_ ASSISTANT
Inheritance Types • Structural – an abstraction that combines two types of structures • ARRAY_STACK is both a STACK and an ARRAY • Facility – abstractions that combine two sets of features • HISTORY and STORABLE • Buttonhole – Combining external models • COMPANY_PLANE, SLEEPING_CAR • Buttons and holes as in the EStudio interface
Feature Renaming • Multiple & repeated inheritance lead to name clashes • What if two parents use the same name for a feature? • A common occurrence since good names are reused • How can the child refer to the appropriate feature? • Answer • Rename one of the features – give it an alias • Do not rely on overloading, not enough variation • distinguish features by argument type and count
Example Renaming • Suppose LONDON and LOS_ANGELES both have the feature foo • Then we can define TORONTO as follows class TORONTO inherit LONDON rename foo as fog end redefine ... end LOS_ANGELES rename foo as smog end redefine ... end feature ... end -- TORONTO
Renaming Effects ldon : LONDON ; la : LOS_ANGELES ; tor : TORONTO Valid – even after polymorphic assignment ldon.foo ; tor.fog la.foo ; tor.smog Invalid ldon.fog ; ldon.smog la.fog ; la.smog tor.foo
Redefinition & Renaming • Redefinition • Keeps the name, changes the semantics • Renaming • Keeps the semantics changes the name • Can both rename and redefine • Rename first • Use new name when redefining • Renaming can be useful to change the name to a more common one for the abstraction • TO push & pop (STACK) FROM add and remove (CONTAINER)
Repeated Inheritance • Indirect • class B inherit Aclass C inherit Aclass D inherit B C • Direct • class B inherit A A A B C D A B
DRIVER age pass_birthday address pay_fee violation_count FRENCH_ DRIVER US_ DRIVER FRENCH_US_DRIVER Problems What about age? It is the same for both drivers! DO NOT rename! Only rename if inheriting different but identically named features Have a single shared feature Sharing is not always appropriate – violation_count, address, pay_fee – are all different – need to replicate for each driver
Repeated Inheritance Rule • In a repeated inheritance • Versions of a repeatedly inherited feature inherited under the same name represent a single feature • Versions inherited under different names represent separate features, each replicated from the original in the common ancestor • Use rename to get replication • rename pay_fee as pay_french_fee • The rule applies to attributes as well as routines
Single Name Rule • Definition • The final name of a feature in a class is • For an immediate feature, the name under which it is declared • For an inherited feature that is not renamed, its final name is (recursively) in the parent from which it is inherited • For a renamed feature, the name resulting from the renaming • Single Name Rule • Two different effective features of a class may not have the same final name
Must Rename • Consider the following attributes, even if the types agree must rename problemin D • Rename either version from B or C or both B problem C problem D
f A ++ f B C D Conflicting Redefinition • In D have two different definitions of f • From B and from A through C • Consider under • sharing • replication
f A ++ f B C D Conflict Resolution – Sharing • Inherit under same name • one version is deferred other is effective • No problem – single name rule • both versions effective but redefined in D • No problem – produce one redefined version • both effective, no redefinition • Problem – name clash, must rename, get replication
Conflict Resolution – Sharing – 2 • Other solutions • Make one of the versions deferred – Other takes over • undefine • Different names – join the solutions • Requires compatible signatures and semantics class D inherit B C undefine f end .... g f B C D class D inherit B C rename g as f undefine f ....
Conflict Resolution – Replication • Suppose a1:= instance of D • Then a1.f is ambiguous • could be either f or bf • Programmer must select the version f A f ++ bf B C D class D inherit B C select f end .... class D inherit B select bf end C ....
Select Rule • A class that inherits two or more different effective versions of a feature from a repeated ancestor and does not redefine them both, must include exactly one of them in a select clause • Use select all end if that is desired
Genericity with Repeated Inheritance • The type of any feature that is shared under the repeated inheritance rule, and the type of any of its arguments if it is a routine, may not be a generic parameter of the class from which the feature is repeatedly inherited • Ambiguity as to the type for f in B. • Use renaming to get replication, if genericity is needed class A[G] feature f : G end class B inherit A [INTEGER] A [REAL] end
Name Clashes – Definition & Rule • In a class obtained through multiple inheritance, a name clash occurs when two features inherited from different parents have the same final name • A name clash makes the class invalidexcept in any of the following cases • The two features are inherited from a common ancestor and none has been redeclared from the version in that ancestor • Both features have compatible signatures and at least one of them is inherited in deferred form • Both features have compatible signatures and they are both redefined in the class • As one redefinition for the feature
Summary of Adaptation Clauses • Eiffel adaptation clauses are in the following order. class B inherit A rename f1 as new_f1, f2 as new_f2, f3 as new_f3 export {A, B} new_f1, f4 redefine new_f2, f5 undefine new_f3, f6 select new_f2, f7 end