TDDD05 Coponent-Based Software DF14900 Software Engineering CUGS Interfaces Design by Contract Syntactic Substitutability Inheritance Considered Harful Fragile Base Class Probles Mixins and View-Based Coposition Christoph Kessler, IDA, Linköpings universitet. 2.2 TDDD05 / DF14900
Interfaces = eans by which coponents connect: Set of naed operations that can be called by clients With specified seantics To be respected both by coponent provider and by client Direct interfaces Procedural interfaces of traditional libraries Directly (explicitly) provided by a coponent Static ethod dispatch Indirect interfaces Object interfaces Provided by the objects instantiated fro the coponent Dynaic ethod dispatch potentially routed to a third coponent Procedural interface ay be odeled as object interface for a static object within the coponent (singleton) 2.3 TDDD05 / DF14900 Coponent (a specialized class) Dictionary spellcheck synonys suppleent Provided interfaces Required interface spellcheck Dictionary synonys suppleent WordProcessor 2.4 TDDD05 / DF14900
2.5 TDDD05 / DF14900 copatible 2.6 TDDD05 / DF14900
2.7 TDDD05 / DF14900 Given a declaration of a variable or paraeter of type X: X x; or (, X x, ) { } Any instance of a class Y that is a descendant of X (including X itself) ay be used as the actual value of x without violating the seantics of the declaration of its use: Y y; x = y; or call (, y, ); Because an Y instance understands all ethods that an X instance ust have. But x.(..) and y.(..) do not necessarily call the sae ethod! (polyorphis) -> syntactic, but not seantic substitutability X ust be sae or a supertype of Y Y ust be sae or a subtype of X (e.g., a superclass) (e.g., a subclass) ACM - Turing Award 2009! Also called Liskov substitutability (attr. to B. Liskov, MIT) 2.8 TDDD05 / DF14900
Subclass as subcontractor : Conforance rules for polyorphic ethod interfaces in OO languages Provider of a subclass ( ) ay expect less than the contract guarantees For input paraeters (foral paraeters): Contravariance and invariance is possible but not covariance Provider of subclass can substitute a supertype (e.g., superclass paraeter type -> ore general type accepted, overfulfills contract For output paraeters (return values, thrown exceptions): Covariance and invariance is possible but not contravariance Provider of subclass can substitute a subtype (e.g., subclass result type -> ore specific type returned, overfulfills contract Workaround ay require downcasts with dynaic type checking ) for a ) for a 2.9 TDDD05 / DF14900 2.10 TDDD05 / DF14900
Deanding a as input paraeter type would break the contract set by the base class. 2.11 TDDD05 / DF14900 Seantic substitutability = conforant types +? Hoare triplets: {precondition} operation {postcondition} [Hoare 69] Preconditions of an operation: True on invocation Callee s / provider s requireents Postconditions of an operation: True on return Callee s / provider s proise to caller / client May be forulated e.g. in UML-Object Constraint Language OCL Deand no ore, provide no less : Contract precondition ust iply provider s precondition Provider s postcondition ust iply contract postcondition 2.12 TDDD05 / DF14900
interface TextModel { int ax(); // axiu length this text can have int length(); // current length char read ( int pos ); // character at position pos void write ( int pos, char ch ); // insert ch at pos // [ len: int, txt: array of char :: // pre len := this.length(); // (all i: 0<=i<len: txt[i] := this.read( i )); // len < this.ax() and 0 <= pos <= len // post this.length() = len + 1 // and (all i: 0<=i<pos: this.read( i ) = txt[i] ) // and this.read( pos ) = ch e x a p l e t e x t // and (all i: pos<i<this.length(): this.read( i ) = txt[i-1] ) ] pos len ax 2.13 TDDD05 / DF14900 class GreatTextModel ipleents TextModel {... // as in TextModel on previous slide e x a p l e t e x t len n pos ax void write ( int pos, char ch ); // insert ch at pos // [ len: int, txt: array of char :: Allow insertions past the end of the current // pre len := this.length(); text (i.e., beyond len) // (all i: 0<=i<len: txt[i] := this.read( i )); by padding with blanks if necessary. // len < this.ax() and 0 <= pos < this.ax() // post this.length() = ax ( len, pos ) + 1 // and (all i: 0<=i< in ( pos, len ): this.read( i ) = txt[i] ) // and this.read( pos ) = ch // and (all i: pos < i <= len: this.read( i ) = txt[i-1] ) ] // and (all i: len < i < pos: this.read(i) = ) ] 2.14 TDDD05 / DF14900
Superclasses (e.g., syste library classes) ay evolve Advantage: bugfixes visible to all subclasses. But... Syntactic Fragile Base Class Proble binary copatibility of copiled classes with new binary releases of superclasses release-to-release binary copatibility Ideally, recopilation should not be necessary in case of purely syntactic changes of superclasses interfaces, e.g.: Methods ay ove upwards in the class hierarchy Methods ay be reoved, replaced, added... Seantic Fragile Base Class Proble How can a subclass reain valid (keep its seantic contract) if functionality inherited fro the superclass evolves? 2.15 TDDD05 / DF14900 syntactic A A () () base class B B call call C C 2.16 TDDD05 / DF14900
class B { // base class int p; char x; public: B s vtable: virtual void () {...} b virtual void ( void ) {... } p 1 virtual int () {... } } b; x 2 class C : B { // subclass of B C s vtable: C s float w; c public: p 1 virtual char* tee() {... } x 2 virtual int () {...} // override w tee } c; 3 tee B s : : Translation of a virtual ethod call in C++: Sae offsets in vtable! soemethod ( B q ) { R1 := q; // self pointer for q passed in R1... // ay pass a B or C... R2 := *q; // vtable address for q s class q.(); R2 := *(R2 + 1*4); // index offset 1 by copiler call R2 2.17 TDDD05 / DF14900 Changed: Method oved up into superclass, new ethod ny added B s : ny class B :A { // refact. base class int p; char x; public: B s vtable: virtual char ny () {... } b virtual void ( void ) {... } p 1 ny virtual int () {... } } b; x 2 3 class C : B { // subclass of B C s vtable:? C s float w; // not recopiled c public: p 1 virtual char* tee() {... } x 2 virtual int () {...} // override w tee } c; 3 tee Stale offset values into C s vtable Not-recopiled virtual ethod call: if C is not recopiled! soemethod( B q ) { R1 := q; // self pointer for q passed in R1... // ay pass a B or C... R2 := *q; // vtable address for q s class q.(); R2 := *(R2 + 1*4); // index offset 1 by copiler call R2 : 2.18 TDDD05 / DF14900
Changed: Method oved up into superclass, new ethod ny added B s : ny class B { // refact. base class int p; char x; public: B s vtable: virtual char ny () {... } b virtual void ( void ) {... } p 1 ny virtual int () {... } } b; x 2 3 class C : B { // subclass of B C s vtable:? C s float w; // not recopiled c public: p 1 virtual char* tee() {... } x 2 virtual int () {...} // override w tee } c; 3 tee : Stale offset values into C s vtable Recopiled virtual ethod call: if C is not recopiled! soemethod( B q ) { R1 := q; // self pointer for q passed in R1... // ay pass a B or C... R2 := *q; // vtable address for q s class q.(); R2 := *(R2 + 2*4); // index offset 2 by copiler call R2 2.19 TDDD05 / DF14900 ECOOP 98, Springer LNCS Class Bag { Correct change Class Bag { char [] bag;... within Bag char [] bag;... void add ( char c ) {... } void add ( char c ) {... } void addall ( char[] ac, int n ) { void addall ( char[] ac, int n ) { evolves /* new ipleentation for (int i=...) self.add( ac[i] );...} without calling add() */... } int cardinality () { return... } int cardinality() {... } but breaks } } the contract of Class CountingBag : Bag { CountingBag Class CountingBag : Bag { int counter;... int counter;... void add ( char c ) { Unchanged void add ( char c ) { counter++; super.add( c ); } (but recopiled) counter++; super.add( c ); } int cardinality() { return counter; } } int cardinality() { return counter;} } 2.20 TDDD05 / DF14900
Replace ipleentation inheritance by object coposition A core coponent is extended by a view coponent Mixin: class fragent used for deriving a subclass Class vs. object level, static vs. dynaic Base class Mixin new subclass Variations on this topic: Mixin-Based Inheritance IBM SOM CoSy generated access layer to IR EJB and Corba CCM Containers + Interfaces Stata-Guttag transforation Subject-Oriented Prograing Object Coposition AOP and Invasive Software Coposition extend operation: this is etaprograing! 2.21 TDDD05 / DF14900 Software coponents need well-defined interfaces and encapsulation Interfaces and Design by Contract Syntactic substitutability, Covariance and Contravariance Operations, pre- and postconditions Deand no ore, provide no less OOP is not the silver bullet for coponent-based software engineering Classes are an overloaded concept: Type (-> super-/subtype conforance), interface/encapsulation, ipleentation inheritance, interface inheritance, object instantiation Ipleentation inheritance and dynaic ethod dispatch break encapsulation (is white-box reuse; but coponents are black boxes ) Contravariance proble for input paraeters Fragile base class proble Possible solutions/workarounds (not perfect either): Taed inheritance by Mixins / View-based Coposition / Object Coposition / SOP / AOP /... 2.22 TDDD05 / DF14900
Szyperski et al.: Coponent Software Beyond Object-Oriented Prograing. 2nd edition, 2002. Chapters 5, 6, 7. Stevens: Using UML, Software Engineering with Objects and Coponents. 2nd edition, Addison-Wesley, 2006. U. Assann: Invasive Software Coposition. Springer, 2003. B. Meyer: Applying Design by Contract. IEEE Coputer, Oct. 1992, pp. 40-51. B. Liskov and J. Wing. Faily Values: A Behavioral Notion of Subtyping. ACM Transactions on Prograing Languages and Systes, Nov. 1994 L. Mikhajlov, E. Sekerinski: A Study of The Fragile Base Class Proble. ECOOP 98, Springer LNCS 1445: 355-382, 1998. W. Harrison, H. Ossher: Subject-Oriented Prograing (A Critique of Pure Objects). ACM OOPSLA 93 pp. 411-428. IBM SOP: www.research.ib.co/sop/ 2.23 TDDD05 / DF14900 2.24 TDDD05 / DF14900