FEN KbP/seminar3: subcontracts1 Kontraktbaseret programmering Seminar 3 Kontrakter og arv Underleverandørsyn Substitutionsprincippet Guarded postcondition
FEN KbP/seminar3: subcontracts2 Underleverandørsyn •Kontraktmæssigt kan en subklasse betragtes som en underleverandør: –Superklassen har indgået en kontrakt med en klient –Superklassen overdrager kontrakten til en underleverandør –Subklassen tager superklassen plads og skal i forhold til klienter opfylde samme kontrakt
FEN KbP/seminar3: subcontracts3 Fig. 6.1 i DbC. Figur fjernet pga. Copyright
FEN KbP/seminar3: subcontracts4 Kontrakt class Courier{ //INVARIANT: forsikring >= 100,000 public void deliver(…); //PREpakkevægt <= 5 kg //POSTleveringstid<= 3 timer }
FEN KbP/seminar3: subcontracts5 Arv og polymorfi •Princip: –I OOPLs kan objekter af en subklasse anvendes, hvor der forventes en forekomst af superklassen –Alt arves – også specifikation, dvs. subklassen overtager forfaderens kontrakt. –Hvis der ændres i kontrakten, så skal substitutionsprincippet overholdes. (”Underleverandør” eller ”subcontractor”)
FEN KbP/seminar3: subcontracts6 Substitutionsprincippet (Babara Liskov, OOPSLA ’87) Man skal altid kunne substituere en forekomst af en afledt klasse ind, hvor der ventes en forekomst af baseklassen. (”Behavioral subtyping”)
FEN KbP/seminar3: subcontracts7 Ændring af kontrakter •Substitutionsprincippet: –Arvingen skal mindst overholde forfaderens kontrakt, dvs. •prebetingelser må ikke strammes •postbetingelser må ikke afsvækkes –Hvis der ændres i kontrakten, så skal substitutionsprincippet overholdes.
FEN KbP/seminar3: subcontracts8 Ny kontrakt //Ny kontrakt: class DifferentCourier extends Courier{ //INVARIANT: forsikring>= 200,000 public void deliver(…); //PREpakkevægt <= 8 kg //POSTleveringstid<= 2 timer } Kan klienter leve med denne kontrakt i stedet for den oprindelige? //Oprindelig kontrakt: class Courier{ //INVARIANT: forsikring >= 100,000 public void deliver(…); //PREpakkevægt <= 5 kg //POSTleveringstid<= 3 timer }
FEN KbP/seminar3: subcontracts9 Substitutionsprincippet class A { //INVARIANT I public void pip(){}; //PRE p //POST q }; p p’ dvs. hvis A.pip()’s PRE er opfyldt, så er B.pip()’s det også q’ qdvs. resultatet af B.pip() opfylder mindst samme betingelser som resultatet af A.pip() I´ I hvis den nye invariant er opfyldt, så er den gamle også class B extends A { //INVARIANT I’ public void pip(){}; //PRE p’ //POST q’ };
FEN KbP/seminar3: subcontracts10 Eksempel: public class Shape{ private int x,y; // figurens position private Colour color; //figurens farve //øvrige attributter public void MoveTo(int newX, int newY){ //PRE: 0 <= newX <= maxX AND 0 <= newY <= maxY, // hvor maxX og maxY angiver vinduets maksimum // POST: x'=newX AND y'=newY } public virtual float Area(){ //PRE: none //POST: Area'= figurens areal med 4 betydende cifre // beregnet efter en eller anden tilnærmet metode} } }//end Shape public class Circle: Shape{ private int r; //radius - x, y //og color arves public override float Area(){ } //øvrige operationer - MoveTo() arves }//end circle Hvad må vi gøre med Area()? Hvad må vi gøre med MoveTo()?
FEN KbP/seminar3: subcontracts11 Dictionary: public interface Dictionary { invariant // Basic queries public int count(); ensures (count()==0) ==> public boolean has(Object key); requires public Object valueFor(Object key); // Derived queries ensures \result == public boolean isEmpty(); // Commands requires ensures ensures ensures public void put(Object key, Object value); requires ensures ensures public void remove(Object key); }
FEN KbP/seminar3: subcontracts12 Designvalg requires ensures ensures ensures public void put(Object key, Object value); Hvad hvis vi tillader, at key er der i forvejen? Dvs. svækker prebetingelsen? Denne del af postbetingelsen holder i så fald ikke længere!
FEN KbP/seminar3: subcontracts13 Guarded postconditions requires ensures ensures (!\old(has(key))) ==> ensures (!\old(has(key))) ==> public void put(Object key, Object value); •Guarded postcondition ændrer ikke klassens specifikation. Det er kun forberedelse på omdefinering i en subklasse. Jf. keyword virtual i C#, som kun muliggør override i en evt. kommende subklasse. Post-betingelsen er bevogtet (guarded) af den tilsvarende prebetingelse Dictionary2
FEN KbP/seminar3: subcontracts14 Designregel •For at understøtte redefinering af metoder i subklasser bør alle postbetingelser beskyttes af deres tilsvarende prebetingelse •Ex: void pip() //PREq //POSTr -- bør være q r
FEN KbP/seminar3: subcontracts15 Guarded postcondition muliggør fx: class A { public void pip(){}; //PRE p //POST p q }; class B extends A { public void pip(){}; //PRE p’ //POST p q //POST p’ q’ }; Prebetingelsen kan afsvækkes Hvis den gamle prebetingelse er opfyldt, så holder den gamle postbetingelse også Ny postbetingelse svarende til den nye prebetingelse kan tilføjes
FEN KbP/seminar3: subcontracts16 Dictionary2: public interface Dictionary2 { invariant // Basic queries public int count(); ensures (count()==0) ==> public boolean has(Object key); requires public Object valueFor(Object key); // Derived queries ensures \result == public boolean isEmpty(); requires ensures ensures (!\old(has(key))) ==> ensures (!\old(has(key))) ==> public void put(Object key, Object value); requires ensures ensures (\old(has(key))) ==> public void remove(Object key); Post-betingelserne er bevogtet (guarded) af den tilsvarende prebetingelse
FEN KbP/seminar3: subcontracts17 Redefinering af Dictionary2 •Vi vil gerne lave et nyt dictionary (RelaxedDictionary2), hvor det bl.a. er muligt: –at kalde put med en key, som allerede er i dictionary’et. I så fald skal put gøre ingenting –at kalde remove med en key, som ikke er dictionary’et. I så fald gør remove ingenting
FEN KbP/seminar3: subcontracts18 Subklassen public interface RelaxedDictionary2 extends Dictionary2 { requires ensures \old(has(key)) ensures \old(has(key)) ==> public void put(Object key, Object value); Den arvede prebetingelse OR’es på: !has(key) OR has(key) true Den arvede post-betingelse AND’es på: !\old(has(key)) ==> valueFor(key)==value AND \old(has(key)) ==> valueFor(key)==\old(valueFor(key)) Nødvendig i JML, når en arving udvider en spec requires ensures !\old(has(key)) ==> public void remove(Object key); }
FEN KbP/seminar3: subcontracts19 Øvelse •Definér kontrakten på Queue med guarded postconditions •Skriv et interface RelaxedQueue med kontrakt, som extend'er Queue og omdefinerer head(), så: 1.det er lovligt at forsøge at udtage et element fra en tom kø, 2.resultatet bliver null, og 3.køen ikke ændres herved. RelaxedQueue
FEN KbP/seminar3: subcontracts20 Specifikation af Queue public interface Queue { invariant // Basic queries public int size(); requires 0<=i && public Object get(int i); // Derived queries requires ensures \result == public Object head(); ensures \result == public boolean isEmpty(); ensures ensures (\forall int i; 0<=i && public Queue shallowCopy(); // Commands ensures ensures public void put(Object o); requires ensures ensures (\forall int i; 0<=i && \old(shallowCopy()).get(i+1) == public void remove(); }
Code Contracts •I Code Contracts er det ike muligt at ændre pre-betingelser (endnu). •Argumentet er, at det ikke er nyttigt (sic!) •Men så undgår man problemer med type co-varians FEN KbP/seminar3: subcontracts21