FEN KbP/seminar2: design11 Kontraktbaseret programmering Seminar 2 Klassedesign – grundprincipper Eksempler: Stack Dictionary
FEN KbP/seminar2: design12 De seks grundprincipper: 1.Adskil forespørgsler og kommandoer 2.Adskil basale forespørgsler fra afledte 3.Specificer postbetingelser for de afledte forespørgsler vha. de basale 4.Specificer kommandoers postbetingelser gennem deres effekt på de basale forespørgsler 5.Specificer prebetingelser på alle operationer 6.Specificer invariante egenskaber i en klasseinvariant Kun de basale forespørgsler er afhængige af datarepræsentationen Konstruktører skal etablere klasseinvarianten. Tilføj repræsentationsinvariant i implementerende klasser
FEN KbP/seminar2: design13 Stakke --Eiffel: Stack[G] Grundlæggende operationer: –count:INTEGER –is_empty:BOOLEAN –intialize –push(g: G) –pop(out g: G) //Java < 1.5 Stack // of Object Stack // fra 1.5 Grundlæggende operationer: –int count() –boolean isEmpty() –Stack() //constructor –void push(Object o) –Object pop()
FEN KbP/seminar2: design14 1: Adskil forespørgsler og kommandoer Postbetingelse på push(Object e): e = = Hvordan udtrykkes dette? e = = pop() dur ikke. Stakken ændres af pop() Vi har brug for at kunne forespørge uden at ændre. (pop gør egentlig to ting: returnerer topelementet og fjerner det)
FEN KbP/seminar2: design15 I stedet top() og remove(): Object top() – returnerer topelementet. Stakken er uændret. (top kaldes item i Eiffel) void remove() – fjerner elementet. Returnerer ikke noget. Forespørgsler returnerer noget, men ændrer ikke objektets tilstand Kommandoer returnerer ikke noget, men kan (gør det som regel) ændre objektets tilstand.
FEN KbP/seminar2: design16 2:Adskil basale forespørgsler fra afledte forespørgsler isEmpty kan udtrykkes vha. count –Postbetingelse på isEmpty: ensures \result == 3:For hver afledt forespørgsel udtryk postbetingelsen ved basale forespørgsler Basale forespørgsler tilgår datarepræsentationen direkte isEmpty() bliver en afledt forespørgsel
FEN KbP/seminar2: design17 Stakkens interface public interface Stack { // Basic queries: public int count(); public Object itemAt(int i); // Derived queries: public boolean isEmpty(); public Object item(); // Commands: public void put(Object e); public void remove(); } Af hensyn til specifikationen er det nødvendigt at have en metode, så man kan tale om alle stakkens elementer Bemærk: item() definerer en logisk rækkefølge mellem elementerne – ikke en fysisk: Vi skal altid kunne returnere sidst ankomne element, så der må være en eller anden logisk orden mellem elementerne svarende til deres ”ankomsttid”.
FEN KbP/seminar2: design18 4:Specificer kommandoer ved deres effekt på basale forespørgsler. 5:Formulér prebetingelser for hver forespørgsel og kommando.
FEN KbP/seminar2: design19 public interface Stack { public invariant // Basic queries public int count(); requires 0<=i && public Object itemAt(int i); // Derived queries ensures \result == public boolean isEmpty(); requires ensures \result == public Object item(); // Commands ensures ensures public void put(Object e); requires ensures public void remove(); } typeinvariant
FEN KbP/seminar2: design110 Klasseinvariant (typeinvariant og repræsentationsinvariant) Udtrykker egenskaber, som ikke ændrer sig i hele objektets levetid For stakkens vedkommende: count() >= 0 Der skal argumenteres for, at klasseinvarianten faktisk er en invariant, dvs. at ingen operation efterlader den falsk: –Objektet skal oprettes i en tilstand, som tilfredsstiller invarianten, dvs. den bliver en del af postbetingelsen på konstruktøren –Kun put og remove har postbetingelser, som siger, at count er ændret: put forøger count remove trækker 1 fra count, men prebetingelser garanterer, at remove kun kaldes med count>=1
FEN KbP/seminar2: design111 Klasseinvarianter Bør udtrykke egenskaber, som går på tværs af operationer Må gerne være redundant i forhold til operationernes specifikationer Bliver en del af både pre- og postbetingelse på alle operationer Skal etableres ved objektoprettelse
FEN KbP/seminar2: design112 Implementation public class LinkedStack implements Stack { private invariant private int count; private Node top; ensures public LinkedStack() { top = null; count = 0; } Repræsentations- invariant Specifikation arves fra interfacet Constructor skal etablere invarianten
FEN KbP/seminar2: design113 public int count() { return count; } public Object itemAt(int i) { Node n = top; while (i<count()-1) { n = n.next; i = i+1; } return n.element; } public boolean isEmpty() { return count==0; } public Object item() { return top.element; } public void put(Object e) { top = new Node(e,top); count = count+1; } public void remove() { top.element = null; top = top.next; count = count-1; } Specifikation er arvet fra interfacet
FEN KbP/seminar2: design114 Object itemAt(int i) Er denne metode ikke et brud med indkapslingen og stakprincippet? Bør den i så fald ikke være private? Nej, en klient kan nemt selv konstruere Object clientItemAt(int i) vha.count, item, remove og put Øvelse: gør det! Løsning1.ppt
FEN KbP/seminar2: design115 Afprøvning af Stack public class StackMain { public static void main(String[] args) { Stack s= new LinkedStack(); for(int i= 0; i<10; i++) s.put(new Integer(i)); System.out.println(s.item()); for(int i= 0; i<10; i++) System.out.println(s.itemAt(i)); } System.out.println(s.itemAt(11)); Hvad sker der her?
FEN KbP/seminar2: design116 De seks principper: 1.Adskil forespørgsler og kommandoer 2.Adskil basale forespørgsler fra afledte 3.Specificer postbetingelser for de afledte forespørgsler vha. de basale 4.Specificer kommandoers postbetingelser gennem deres effekt på de basale forespørgsler 5.Specificer prebetingelser på alle operationer 6.Specificer invariante egenskaber i en klasseinvariant Kun de basale forespørgsler er afhængige af datarepræsentationen Konstruktører skal etablere klasseinvarianten Implementerende klasser skal specificere en repræsentationsinvariant Programmer mod et interface. Specificer en typeinvariant
FEN KbP/seminar2: design117 A side: Programmér altid mod interfaces ArrayList l = new ArrayList(); List l = new ArrayList(); List l = ListFactory.createList(); class ListFactory{//FactoryMethod public static List createList(){ return new ArrayList(); } }
FEN KbP/seminar2: design118 Dictionary: (Map – værdibaseret container, lagrer par af (key, value)) 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/seminar2: design119 Designvalg requires ensures ensures ensures public void put(Object key, Object value); Må key være null? Hvad hvis vi tillader, at key er der i forvejen? ensures (count()==0) ==> public boolean has(Object key); requires ensures ensures public void remove(Object key); Hvad hvis vi tillader, at key ikke findes?