Objektorienterad modellering och design (EDAF25) Föreläsning 3 Seminarierna Instruktioner Agenda UML objekt och sekvensdiagram Design smells Designprinciper (DRY, SRP, OCP, DIP) Att göra denna och nästa vecka: Läs Martin 7-9,11 Del 1 av kursen redovisas på fredagens seminarium (S1)! Bonuspoäng Eventuella bonuspoäng kan uppgå till högst 2p per delområde. Inga inlämningar, endast stickprov En av uppgifterna i övningshäftet redovisas skriftligt på plats. Ytterligare några uppgifter redovisas muntligt och diskuteras gemensamt. Bonuspoäng ges enligt följande: summan av poängen på de uppgifter du kryssat i för muntlig redovisning (max 1p) och poängen på den skriftliga redovisningen (max 1p). EDAF25 (F3) VT 2018 1 / 47 Seminarierna Instruktioner EDAF25 (F3) VT 2018 2 / 47 Övningarna Viktiga relationer Praktiskt på seminariet Lägg undan allt utom pennor och ID. Kryssa för vilka av uppgifterna du vill redovisa. Lös din uppgift (med svart eller grå penna). Skriv ditt namn! Du kommer sedan att rätta någon annans uppgift (med röd penna). I slutet av seminariet tittar du på rättningen av din egen uppgift och godkänner den genom att lämna in uppgiften till läraren. Tycker du att kamratgranskningen var fel, skriver du varför i en kommentar på baksidan av uppgiften innan du lämnar in den. Min frimärkssamling är itererbar vilket innebär att den har en metod för att skapa iteratorer 1 riktning på beroendet 2 typ av beroende (har/är) 3 grad av beroende «interface» Iterable Stamp Collection «interface» Iterator StampCollection Iterator Vill du inte redovisa någon uppgift använder du tiden till att själv jobba med något av problemen, innan vi börjar med den gemensamma diskussionen. EDAF25 (F3) VT 2018 3 / 47 EDAF25 (F3) VT 2018 4 / 47
UML Objektdiagram UML Objektdiagram (1+2) + 3 EDAF25 (F3) VT 2018 5 / 47 UML Sekvensdiagram EDAF25 (F3) VT 2018 6 / 47 UML Sekvensdiagram EDAF25 (F3) VT 2018 7 / 47 EDAF25 (F3) VT 2018 8 / 47
Aktivitet UML Sekvensdiagram UML Sekvensdiagram (1+2) + 3 Visa beräkning av (1 + 2) + 3 i ett sekvensdiagram. Martin EDAF25 (F3) VT 2018 9 / 47 Principles EDAF25 (F3) VT 2018 10 / 47 Principles (11st - lista längst bak i insidan av pärmen iboken +1), Patterns (9st - av 23 i listan innan Section 1 i boken + 1), and Practices (andra kurser) SRP - The Single Responsibility Principle OCP - The Open-Closed Principle DIP - The Dependency Inversion Principle DRY - Don t Repeat Yourself Principle ISP - The Interface Segregation Principle LSP - The Liskov Substitution Principle REP - The Release-Reuse Ecuivalency Principle CCP - The Common Closure Principle CRP - The Common Reuse Principle ADP - The Acyclic Dependencies Principle SDP - The Stable Dependencies Principle SAP - The Stable Abstraction Principle EDAF25 (F3) VT 2018 11 / 47 EDAF25 (F3) VT 2018 12 / 47
Design smells Agil design Stelhet (Rigidity) Designen är svår att modifiera Bräcklighet (Fragility) Designen tål inte modifiering Orörlighet (Immobility) Designen förhindrar återanvändning Seghet (Viscosity) Det är svårare att göra snygga ändringar än "hack" Agil praxis -> Identifikation av ett problem Designprinciper -> Diagnosticering av problemen Designmönster -> Lösning av problemet Exempel i Martin s.90-94 "Kopieringsprogrammet" EDAF25 (F3) VT 2018 13 / 47 Mått på designkvalitet EDAF25 (F3) VT 2018 14 / 47 Exempel på dålig design Koppling (Coupling) - Graden av beroenden till andra moduler i systemet Sammanhang(Cohesion) - Graden av samhörighet mellan medlemmarna i samma modul public class CompetitionHandler { // or TotalComputation private Competition competition; starttime = competition.getraces()[i].getresults()[j]. getstarttime().getseconds(); endtime = competition.getraces()[i].getresults()[j]. getendtime().getseconds(); competition.getraces()[i].getresults()[j].getresults()[j]. settotaltime(endtime - starttime); EDAF25 (F3) VT 2018 15 / 47 EDAF25 (F3) VT 2018 16 / 47
Fortfarande dålig Competition Lokalitetsprinicpen! public class CompetitionHandler { private Competition competition; result = competition.getraces()[i].getresults()[j]; starttime = result.getstarttime().getseconds(); endtime = result.getendtime().getseconds(); result.settotaltime(endtime - starttime); public class Competition { private ArrayList<Race> races; public void computetotal() { for(race race: races ) { race.computetotal(); Delegerar arbetet till den klass som vet allt om Race: EDAF25 (F3) VT 2018 17 / 47 Race Lokalitetsprinicpen! EDAF25 (F3) VT 2018 18 / 47 SRP Enkelt ansvar public class Race { private ArrayList<Result> results; public void computetotal() { for(result result: results) { result.computetotal(); Delegerar arbetet till den klass som vet allt om Result: Single Responsibility Principle A class should have only one reason to change. (Martin) En klass med flera ansvarsområden ger bräcklighet. En klass utan ansvar är onödig. EDAF25 (F3) VT 2018 19 / 47 EDAF25 (F3) VT 2018 20 / 47
Time har bara ett ansvarsområde SRP Enkelt ansvar Result har flera SRP Enkelt ansvar public class Time implements Comparable<Time> { private int sec; public Time difference(time other) public int compareto(time other) public String tostring() public Time(String time) public class Result { private String firstname, secondname; private String idnumber; private int starttime, endtime; public String fullname(){ private boolean checkidnumber(){ public int totaltime() { return endtime - starttime; EDAF25 (F3) VT 2018 21 / 47 Delegera ansvaren! SRP Enkelt ansvar EDAF25 (F3) VT 2018 22 / 47 GenerateCode har massor av ansvar SRP Enkelt ansvar public class Result { private Name name; private IdNumber idnumber; private Time start, end; public String fullname() { return name.tostring(); public Time total() { return end.difference(start); public static void generatecode(instruction c) { switch (c.opcode) { case 0: //MOV if (!(c.arg1 instanceof Current c.arg1 instanceof Next) &&!(c.arg2 instanceof Current c.arg2 instanceof Next { out.writebytes( mov + code.convert(c.arg1) +, + code.convert(c.arg2) + \n ); else if ((c.arg1 instanceof IntConst c.arg1 instanceof BoolConst) && c.arg2 instanceof Current) { out.writebytes( mov + code.convert(c.arg1)+, + code.savetoreg(c.arg1) + \n ); out.writebytes( set + code.convert(c.arg2,staticlevel 1) +, + code.savetoreg(c.arg2) + \n ); out.writebytes( st + code.savetoreg(c.arg1) +, + [ + code.savetoreg(c.arg2) + ]\n ); else if((c.arg1 instanceof Temp) && c.arg2 instanceof Current) { out.writebytes( set + code.convert(c.arg2,staticlevel 1) +, + code.savetoreg(c.arg2) + \n ); out.writebytes( st + code.savetoreg(c.arg1) +, + [ + code.savetoreg(c.arg2) + ]\n ); else if(c.arg1 instanceof Current && c.arg2 instanceof Temp) { out.writebytes( set + code.convert(c.arg2) +, + code.savetoreg(c.arg2) + \n ); out.writebytes( ld + [ + code.savetoreg(c.arg2) + ] +, + code.savetoreg(c.arg1) + \n ); else if(c.arg1 instanceof Current && c.arg2 instanceof Current) { if(!(c.arg1.tostring().equals(c.arg2.tostring()))) { out.writebytes( set + code.convert(c.arg1,staticlevel 1) +, + code.savetoreg(c.arg2) + \n ); EDAF25 (F3) VT 2018 23 / 47 EDAF25 (F3) VT 2018 24 / 47
Vilka ansvarsområden? SRP Enkelt ansvar Aktivitet Gör en bättre design! Kodgenerering för: MOV-instruktionen OR-instruktionen Current-operander Temp-operander EDAF25 (F3) VT 2018 25 / 47 DRY Undvik upprepning EDAF25 (F3) VT 2018 26 / 47 Don t Repeat Yourself Principle Every piece of knowledge must have a single, unambiguous, authoritative representation within a system (Hunt and Thomas) Open/Closed Principle Classes should be open for extension and closed for modification. (Meyer) Det skall vara möjligt att lägga till ny funktionalitet utan att modifiera existerande kod. EDAF25 (F3) VT 2018 27 / 47 EDAF25 (F3) VT 2018 28 / 47
Expr-klasserna tillämpar Open/Closed-principen Aktivitet Man kan lägga till nya uttryck utan att ändra något i de gamla. Man kan lägga till Sub, Mul och Div. Aktivitet: Lägg till en Abs-operation som ger absoluta värdet av ett uttryck och implementera value()! public class Abs implements Expr { private Expr expr; public int value(){ return Math.abs(expr.value()); EDAF25 (F3) VT 2018 29 / 47 Dålig design: Circle, Square EDAF25 (F3) VT 2018 30 / 47 Dålig design: Figure public class Circle { int radius; int x, y; public Circle(int radius, int x, int y) { this.radius = radius; this.x = x; this.y = y; Square är analog. public class Figure extends ArrayList { public Figure() { add(new Square(4, 1, 1)); add(new Circle(4, 2, 2)); public void draw() { for (Object object: this) { if (object instanceof Square) { drawsquare((square) object); else { drawcircle((circle) object); EDAF25 (F3) VT 2018 31 / 47 EDAF25 (F3) VT 2018 32 / 47
Dålig design: Figure Dålig design: Figure public class Figure { private static void drawcircle(circle circle) { // TODO Implement method private static void drawsquare(square square) { // TODO Implement method ArrayList Figure + draw(): void + drawcircle(circle): void + drawsquare(square): void Square Circle EDAF25 (F3) VT 2018 33 / 47 Circle & Shape & Figure EDAF25 (F3) VT 2018 34 / 47 Circle & Shape & Figure public class Circle implements Shape { private int radius; private int x, y; public void paint(graphics graphics) { // TODO Auto-generated method stub public interface Shape { public void paint(grahpics graphics); public class Figure extends ArrayList<Shape> { public void paint(graphics graphics) { for (Shape shape : list) { shape.paint(graphics); ArrayList<Shape> Figure + paint(graphics) Circle «interface» Shape + paint(graphics) Square EDAF25 (F3) VT 2018 35 / 47 EDAF25 (F3) VT 2018 36 / 47
DIP Bero på abstraktion Figure: Dålig design DIP Bero på abstraktion Dependency Inversion Principle High level classes should not depend on low level classes; both should depend on abstractions. Abstractions should not depend on details. Details should depend on abstractions (Martin) UML-pilar skall gå mot gränssnitt och abstrakta klasser. ArrayList Figure + draw(): void + drawcircle(circle): void + drawsquare(square): void Square Circle EDAF25 (F3) VT 2018 37 / 47 Bättre! DIP Bero på abstraktion EDAF25 (F3) VT 2018 38 / 47 Namngivning ArrayList<Shape> Figure + paint(graphics) Circle «interface» Shape + paint(graphics) Square Bra namngivning är fundamental för att göra program begripliga. public class MyList { private List<int[]> thelist; public List<int[]> getlist() { List<int[]> list = new ArrayList<int[]>(); for (int[] is : thelist) { if (is[0] < 4) { list.add(is); return list; Vad handlar det om? EDAF25 (F3) VT 2018 39 / 47 EDAF25 (F3) VT 2018 40 / 47
Namngivning Namngivning Det handlar om projektgrupper. public class Groups { private List<Group> groups; public List<Group> incompletegroups() { List<Group> list = new ArrayList<Group>(); for (Group group : groups) { if (! group.iscomplete()) { list.add(group); return list; En grupp ska bestå av fyra studenter. public class Group extends ArrayList<Student> { private static final int MAXSIZE = 4; public boolean iscomplete() { return size() >= MAXSIZE; I vilka grupper finns det plats för ytterligare studenter? EDAF25 (F3) VT 2018 41 / 47 Namngivning EDAF25 (F3) VT 2018 42 / 47 Namngivning Klasser De reviderade klasserna behöver knappast någon särskild dokumentation. MyList gör det: MyList innehåller en lista av grupper av studenter. En grupp representeras med en heltalsarray där det första elementet innehåller antalet studenter i gruppen och de övriga innehåller id-nummer för studenterna. Metoden getlist returnerar en lista med de grupper som har plats för ytterligare studenter. Ett klassnamn är normalt ett substantiv hämtat från uppdragsgivarens beskrivning eller den verklighet som programmet modellerar. Namnet inleds med en versal och följs av gemener. På engelska använder man versal i början på varje ord när namnet är sammansatt. EDAF25 (F3) VT 2018 43 / 47 EDAF25 (F3) VT 2018 44 / 47
Namngivning Gränssnitt Namngivning Metoder Samma principer som för klassnamn. Namn på gränssnitt används oftare än namn på konkreta klasser; därför är det viktigare att dessa namn är bra. Undvik att indikera att det handlar om gränssnitt som i ITree TreeInterface. Hierarkin som IList, AbstractList och ArrayList är bra när alla nivåerna behövs. Metodnamn inleds med en gemen och följande ord i sammansatta namn med versal. Metoder som förändrar objektets tillstånd bör ha ett verb som namn. Metoder som inte förändrar objektets tillstånd bör ha ett substantiv som namn eller inledas med is om de implementerar ett predikat. Namn som inleds med set och get bör reserveras för de tillfällen då det handlar om att sätta och hämta attribut. EDAF25 (F3) VT 2018 45 / 47 Namngivning Attribut, lokala variabler och parametrar EDAF25 (F3) VT 2018 46 / 47 Ett attribut kan ofta ha samma namn som sin typ, fast med inledande gemen. Om det finns flera attribut av samma typ som används på samma sätt kan man numrera dem: Expr expr1, expr2; När en variabel används som index i en array använder man gärna konventionella namn som i och j. För strängar använder man gärna namn som indikerar användningen: String title; Använd inte the som ett prefix i attributnamn. När det behövs är this det självklara alternativet. EDAF25 (F3) VT 2018 47 / 47