Objektorienterad modellering och diskreta strukturer (EDAF10/EDA061) HT1 2013, FÖRELÄSNING 4 Förra föreläsningen Designmönster: Command Composite Template Method Strategy UML: Objekt- och Sekvensdiagram Projektet Computer I pausen: Kursombud hittar ni nu på hemsidan Projektgrupper EDAF10/EDA061 HT2013, Ulf Asklund 1
Dagens agenda Projektgrupper klara?! Redovisningstid inbokad? Designmönster Strategy (exempel) Singleton Null object Facade Decorator ISP Interface Segregation Principle Muterbar vs. Ej muterbar Intro till Payroll Jojo-kort med två strategier public class TravelCard { private Tariff tariff; public double price(int zones, Rebate rebate) { double amount = tariff.price(zones); return rebate.price(amount); public void settariff(tariff tariff) { this.tariff = tariff; EDAF10/EDA061 HT2013, Ulf Asklund 2
Två strategi-interface public interface Tariff { public double price(int zones); public interface Rebate { public double price(double price); Två strategier public class Skane implements Tariff { public double price(int zones) { switch (zones) { case 1: return 17.0; default: return 7.0 * zones + 7.0; public class Family implements Rebate { public double price(double price) { return 1.5 *price; EDAF10/EDA061 HT2013, Ulf Asklund 3
Strategierna har inget tillstånd public final static Tariff SKANE = new Skane(); public final static Rebate DUO = new Family();... TravelCard jojo = new TravelCard(); jojo.settariff(skane); double price = jojo.price(3, DUO); Template method eller Strategy? Båda mönstren kan användas för att eliminera duplicerad kod. Använd Template method när funktionaliteten skall vara den samma under objektets hela livstid. Använd Strategy när funktionaliteten skall kunna förändras under livstiden eller när det finns mer än en funktionalitet som kan variera. EDAF10/EDA061 HT2013, Ulf Asklund 4
Singleton Syfte: att det bara skall kunna skapas en instans av klass Hackerns favoritmönster Konventionell Singleton public class Singleton { private static Singleton instance; // attributes omitted private Singleton() { // omissions public static Singleton instance() { if (instance == null) { instance = new Singleton(); return instance; // other methods omitted EDAF10/EDA061 HT2013, Ulf Asklund 5
Singleton Bieffekt av den konventionella implementeringen: instansen blir ett globalt åtkomligt objekt. Globala objekt är bekväma att använda men innebär i regel dålig design. Konventionell Singleton får inte användas i kursen för klasser som har ett tillstånd! På nästa övning skall vi implementera Singleton-mönstret utan att skapa globalt tillgängliga objekt. Konventionell länkad lista public class Node { private Object object; private Node next; public class List { private Node first; public int length() { int length = 0; Node node = first; while (node!= null) { length++; node = node.next(); return length; NULL Detta är en maskerad form av instanceof. next()är en getter. Node saknar intelligens. EDAF10/EDA061 HT2013, Ulf Asklund 6
Den skyldige: Tony Hoare EDAF10/EDA061 HT2013, Ulf Asklund 7
Null Object Introduktionen av null i objektorienterade språk var ett fundamentalt misstag. null modellerar något som inte finns. Länkade listor och träd avslutas ofta med null. Null är inget objekt; att använda null för att representera ingenting är ett exempel på icke-objektorienterad programmering. Null object Programkoden blir enklare om man istället använder ett riktigt objekt. Detta är ett exempel på mönstret Null Object. Objektorienterad beskrivning: En lista är antingen tom eller består av en nod med ett element som är ett Object och en svans som är en lista. EDAF10/EDA061 HT2013, Ulf Asklund 8
Objektorienterad representation public interface List { public int length(); public class Empty implements List { public int length() { return 0; public class Node implements List{ private Object object; private List tail; public int length() { return 1 + tail.length(); Objektorienterad listimplementering Man ersätter iteration med rekursion; det gör programmet logiskt enklare; testet av det logiska villkoret i while-satsen försvinner. I nästan alla sammanhang är det bra design att representera något som är tomt med ett riktigt objekt. EDAF10/EDA061 HT2013, Ulf Asklund 9
ISP - Segregation Interface Segregation Principle Classes should not be forced to depend on methods that they do not use. Java.util.Collection public interface Collection<E> extends Iterable<E> { int size(); boolean isempty(); boolean contains(object o); Iterator<E> iterator(); Object[ ] toarray(); <T> T[ ] toarray(t[ ] a); boolean add(e e); boolean remove(object o); boolean containsall(collection<?> c); boolean addall(collection<? extends E> c); boolean removeall(collection<?> c); void clear(); boolean equals(object o); int hashcode(); EDAF10/EDA061 HT2013, Ulf Asklund 10
Collection javadoc The destructive methods contained in this interface, that is, the methods that modify the collection on which they operate, are specified to throw UnsupportedOperationException if this collection does not support the operation. If this is the case, these methods may, but are not required to, throw an UnsupportedOperationException if the invocation would have no effect on the collection. For example, invoking the addall(collection) method on an unmodifiable collection may, but is not required to, throw the exception if the collection to be added is empty. [Josh Bloch, Neal Gafter] Collection bryter mot ISP Alla som implementerar gränssnittet måste ha metoder som inte behöver fungera. Hur borde det se ut? EDAF10/EDA061 HT2013, Ulf Asklund 11
Hur borde det se ut? public interface Collection<E> extends Iterable<E> { int size(); boolean isempty(); boolean contains(object o); Iterator<E> iterator(); Object[ ] toarray(); <T> T[ ] toarray(t[ ] a); boolean containsall(collection<?> c); boolean equals(object o); int hashcode(); Hur borde det se ut? public interface ImmutableCollection<E> extends Collection<E> { public interface MutableCollection<E> extends Collection<E> { boolean add(e e); boolean remove(object o); boolean addall(collection<? extends E> c); boolean removeall(collection<?> c); void clear(); EDAF10/EDA061 HT2013, Ulf Asklund 12
Vad tycker Josh och Neal? Jag tror att de håller med. De har skrivit en bok som visar att Java har många brister och konstigheter. Joshua Bloch, Neal Gafter: Java Puzzlers Traps, pitfalls, and corner cases. Addison-Wesley, 2005, ISBN 0-321-33678-X. Länk: www.javapuzzlers.com Vad händer? public class Elimentary { public static void main(string arg[ ]) { System.out.println(12345 + 5432l); 1 Kompileringsfel. 2 Skriver 66666. 3 Skriver något annat. Utskriften blir 17777 eftersom det sista tecknet i det andra talet är bokstaven l som anger att det skall representeras som en long. Det numeriska värdet är 5432. EDAF10/EDA061 HT2013, Ulf Asklund 13
Kort repetition Integritetsprincipen Gör attribut, metoder och klasser så hemliga de går. Lämna inte ut representationen i onödan. En klass skall inte tillgång till klasser som den inte behöver. Begränsa synligheten genom att använda : private bara klassen kan se paketsynlighet, klasser i paketet kan se protected subklasser och klasser i paketet kan se. public hela världen kan se. EDAF10/EDA061 HT2013, Ulf Asklund 14
Exponera inte representationen Exponerad: public class Figure extends ArrayList<Shape> { public void paint(graphics graphics) { for (Shape shape : this) { shape.paint(graphics); Ej exponerad: public class Figure { private List<Shape> list = new ArrayList<Shape>(); public void paint(graphics graphics) { for (Shape shape : list) { shape.paint(graphics); Exponera inte representationen, forts. Utlämnad representation: public class Figure { private List<Shape> list; public void paint(graphics graphics) { for (Shape shape : list) { shape.paint(graphics); public List<Shape> list() { return list; EDAF10/EDA061 HT2013, Ulf Asklund 15
(nästan) Förbud instanceof static getters Men det finns tillfällen när instanceof, static och getters är nödvändiga och det enda rätta:... instanceof public final class Integer extends Number implements Comparable<Integer> { private final int value; public boolean equals(object object) { if(object instanceof Integer) { Integer other = (Integer) object; return value == other.value; return false; // omissions Här finns inget bättre sätt. EDAF10/EDA061 HT2013, Ulf Asklund 16
static public static main(string arg[]) public final static double PI public static double sin(double a) public class Outer { private static class Inner { Språkets design kräver static main. Ibland finns det inget tillhörande objekt. Inre klasser bör helst vara static. getters Det finns ett exempel i Computer-projektet där en getter ger en bättre helhetsdesign. Observer-mönstret behöver i regel getters. Ibland måste man kompromissa när principerna drar åt olika håll. EDAF10/EDA061 HT2013, Ulf Asklund 17
Spekulativ design Fool me once shame on you Fool me twice shame on me. Skriv program som om förutsättningarna inte kommer att förändras. Om detta ändå sker så implementera abstraktioner som skyddar mot framtida förändringar av samma slag. Take the first bullet and protect yourself from the second bullet from the same gun. Exempel Uppdragsgivaren vill ha en lista av Item-objekt: EDAF10/EDA061 HT2013, Ulf Asklund 18
Exempel, forts. Uppdragsgivaren vill senare ha en lista som innehåller Item1- objekt och Item2-objekt. Detta är första skottet; nu garderar vi oss mot ett liknande skott: TANSTAAFL TANSTAAFL There ain t no such thing as a free lunch. (Martin, sidan 319) Att införa ett designmönster är inte gratis. Det måste finnas ett bra skäl att använda det. T ex så använder man inte strategimönstret om det inte finns minst två strategier. EDAF10/EDA061 HT2013, Ulf Asklund 19
Muterbart vs. Ej muterbart Muterbara vs. Ej muterbara objekt Ett objekt vars tillstånd kan förändras kallas muterbart (mutable). När objektet modellerar någonting i verkligheten som har ett tillstånd som kan förändras så är det rimligt att modellen har samma egenskap. EDAF10/EDA061 HT2013, Ulf Asklund 20
Muterbara vs. Ej muterbara objekt, forts Ett objekt vars tillstånd inte kan förändras kallas omuterbart (immutable). När ett objektet modellerar någonting i verkligheten vars tillstånd inte kan förändras så är det rimligt att modellen har samma egenskap. Integer-objekt och String-objekt är omuterbara. Exempel En omuterbar klass: public final class Num implements Expr { private int value; public value() { return value; En muterbar klass: public class Counter { private int counter; public increment() { counter++; EDAF10/EDA061 HT2013, Ulf Asklund 21
Recept för omutbarhet Klassen måste vara final. Attributen måste vara privata. Attributen får ej vara muterbara. Inga metoder får förända attribut. Fördelar: Säkrare Behöver ej kopieras, kan delas Enklare att resonera om Aktivitet public final class SafeContainer { private final Object object; public SafeContainer(Object object) { this.object = object; public String tostring() { return object.tostring(); Är klassen muterbar? EDAF10/EDA061 HT2013, Ulf Asklund 22
Aktivitet - lösning List<String> list = new ArrayList<String>(); SafeContainer safecontainer = new SafeContainer(list); list.add( string ); Klassen SafeContainer är muterbar. Payroll - fallstudie EDAF10/EDA061 HT2013, Ulf Asklund 23
Systembeskrivning Vissa anställda arbetar på timbasis. Deras timlön finns i anställningsposten. De lämnar in tidkort med datum och antal timmar. Lönen utbetalas fredagar. Vissa anställda har fast lön som utbetalas sista vardagen varje månad. Vissa anställda med fast lön får också provision baserad på kvitton med datum och belopp med löneutbetalning varannan fredag. Lön utbetalas antingen via postanvisning till angiven adress, direkt till konto på bank, eller avhämtas på lönekontoret. Systembeskrivning Vissa anställda tillhör fackföreningen. Deras anställningskort innehåller veckoavgift. Föreningen kan debitera serviceavgifter för enskilda medlemmar. Avgifter dras från nästa lön. Transaktioner till systemet behandlas en gång om dagen och uppdaterar en databas. Löneutbetalningar genereras varje avlöningsdag. EDAF10/EDA061 HT2013, Ulf Asklund 24
Användningsfall 1 Lägg till en ny anställd. 2 Ta bort en anställd 3 Registrera ett tidkort 4 Registrera ett försäljningskvitto 5 Registrera en föreningsavgift 6 Ändra anställningsinformation 7 Generera löneutbetalningar Use case diagram EDAF10/EDA061 HT2013, Ulf Asklund 25
Use case diagram med arv Martins bok övergripande design kapitel 18 i Martin Implementering kapitel 19 i Martin med C++. Implementering med Java i Payroll.zip via föreläsningssidan. EDAF10/EDA061 HT2013, Ulf Asklund 26
Databasen enligt Martin Databasen är en implementeringsdetalj. Moduler skall bero på abstraktioner (DIP). Definiera ett gränssnitt (API)! Facade-mönstret Employee DB + get(int): Employee + put(int, Employee): Employee + remove(int): Employee Application Generic DB + <many generic methods> + EDAF10/EDA061 HT2013, Ulf Asklund 27
Designmönster - Facade Databas implementering för testning public interface Database { public Employee put(int key, Employee employee); public Employee get(int key); public Employee remove(int key); public class TestDatabase implements Database { private Map<Integer, Employee> employees = new HashMap<Integer, Employee>(); public Employee get(int empid) { return employees.get(empid); public Employee remove(int empid) { return employees.remove(empid); public Employee put(int empid, Employee employee) { return employees.put(empid, employee); EDAF10/EDA061 HT2013, Ulf Asklund 28
Employee - Det finns tre sorters anställda Design utan eftertanke Vad är problemet? Användningsfall: en timanställd blir löneanställd Vad är problemet? Om en timanställd blir löneanställd så måste man skapa ett nytt objekt och kopiera data från det gamla. Bättre design? Designmönster? EDAF10/EDA061 HT2013, Ulf Asklund 29
Strategy Anställningsformen är en strategi Employee EDAF10/EDA061 HT2013, Ulf Asklund 30
Decorator-mönstret Skånetrafiken vill få en lista på alla köp av biljetter under oktober. Standardautomaten: public interface PaySation { public double pay(travelcard travelcard, int zones); public StandardPayStation implements PayStation { private double sum; public double pay(travelcard travelcard, int zones) { double price = travelcard.price(zones); sum += price; return price; Decorator-mönstret Den nya funktionaliteten: public LoggingPayStation implements PayStation { private PayStation paystation; private List<String> log; public LoggingPayStation(PayStation paystation, List<String> log) { this.paystation = paystation; this.log = log; public double pay(travelcard travelcard, int zones) { log.add(travelcard.tostring() + zones); return paystation.pay(travelcard, zones); EDAF10/EDA061 HT2013, Ulf Asklund 31
Loggningen kan göras dynamiskt PayStation standardpaystation = new StandardPayStation(); PayStation paystation = standardpaystation; I början av oktober lägger vi till loggningen: paystation = new LoggingPayStation(standardPayStation); I slutet av oktober tar vi bort den: paystation = standardpaystation; Tillståndet i standardpaystation bevaras. Decorator-mönstret i Reader-klasserna reader = new BufferedReader(new FileReader(fileName)); public class BufferedReader extends Reader { private Reader in; // omissions EDAF10/EDA061 HT2013, Ulf Asklund 32
Decorator EDAF10/EDA061 HT2013, Ulf Asklund 33