F5 Kodkvalitet Programutveckling sker i faser Livscykel

F5
Kodkvalitet
151
Programutveckling sker i faser
• Här: starkt förenklat version
• Passar bara mindre projekt
• Fem delmoment:
– Fastställa och analysera förutsättningarna/kraven
– Skapa en design
– Implementera koden
– Testning
– Dokumentation
OBS! Testning och dokumentation ska ske
152
parallellt med de övriga momenten.
Livscykel, vidareutveckling och
återanvändning
Scenario 1: Nyproduktion av ett hus, som ska
fungera som skola
Scenario 2: Ombyggnation av ett modernt hus,
byggt 2010 används som en förskola och ska
fortsätta att användas som F-9 skola
Scenario 3: Ombyggnation av ett gammal Kmärkt skola byggt 1870, till kontor
Scenario 4: Åtgärda en skola som är klassat
som ett sjukt hus. Skolan är bygg 1990. 153
Legacy code - ärvd kod
Skriva kod som lever i flera generationer och varianter,
som andra än du själv jobbar med.
Ändra/jobba med kod som andra (eller du själv) har
utvecklat, tex.
»
Bygga vidare
Ny version
»
Kundanpassa
»
»
Rätta fel
154
Legacy code - ärv kod
Vad är viktigt då för att minimera kostnader och
risker?
Systemen/koden är lätt att förstå
Lätt att testa det nya utan att ha sönder det gamla
Samt
Färdigheten att läsa och förstå andras system och kod,
155
dokumentation, etc.
Livscykel, vidareutveckling och
återanvändning
Scenario 1: Nytt system som ska byggas från
grunden
Scenario 2: Utökning av funktionalitet i RSV
system för folkbokföring som påbörjades 1985
Scenario 3: Anpassning ett system mot en helt
ny kund, systemet utvecklat sedan 2010
Scenario 4: Jobba bort felrapporter som
kommer in på mobiltelefonsystem, för att
komma ut med en helt ny release.
156
Sträva efter kod som är…
• kod som är bra, dvs, hög kodkvalitet
• kod som är man kan stå för, för man kan
garantera att den gör det den ska göra och
inget annat, bra testad
• kod som är beskriven på ett bra och tydligt
sätt, dvs bra dokumenterad
157
Kodkvalitet
Vad är god kodkvalitet?
Vad är dålig kodkvalitet?
Hur ”mäter vi kodkvalitet”?
Vem bär ansvaret för kvalitet på koden?
158
Kodkvalitet
Hur kan man jobba för att nå hög kvalité?
Vems ansvar är det om en klass eller metod beter
sig konstigt och orsakar fel?
159
Hur kan man jobba med kodkvalité?
1. Jobba strukturerat genom hela
processen
160
Hur kan man jobba med kodkvalité?
•Fokusera på kvalité och inte kvantitet
– Bygg det ”viktigaste” först
•Jobba tillsammans
– Gemensamt ansvar en för alla alla för en
– Kontinuerlig uppföljning av processen
•Kod-granskning
•Lätt att läsa, lätt att testa, förklara
•Koden följer grundläggande kodprinciper
•Koden, systemet är inte onödig svårt att förstå
•Involvera testning i ett tidigt skede av processen
161
Hur kan man jobba med kodkvalité?
•Tydlighet i förväntningar
• Tydliga krav vad som ska systemet ska kunna göra
• Tydliga definitioner när en funktion/del fungerar som den ska
både i det lilla och i det stora
• Tydliga definitioner på när det inte fungerar (undantag, fel, etc)
•Bra design
• System/klassdesign/metoddesign
• Systemarkitektur, klasser, metoder, etc
•Dokumenterad testning på många nivåer
• Enhets, integrering, system,…acceptans-test
• Hur ska man följa upp/garantera att koden fungerar även efter
flera generationer
• Mer om det senare
162
163
Johan Eliasson
F6
Klass och metoddesign
165
Hur kan man jobba med kodkvalité?
1. Jobba strukturerat genom hela
processen
166
Skapa en OO-design
• Bestäm klasser, objekt och metoder som behövs
– Vad finns redan?
• Bestäm algoritmer för problemlösningen
• I princip oberoende av programmeringsspråk
– Diagram
– Pseudokod
• Designa för återanvändning?
– Det är svårare att göra generella lösningar
– Kan löna sig i framtiden
– Återanvändning har varit en stor anledning till OOboomen
167
Implementation
• När man kommer till detta steg så har man ”ritning
klar”, det mesta av ”materialvalen är gjorda”
• Översättning av design till källkod
• Implementationen fokuserar på kod-detaljer
• Alla viktiga beslut tas vid analys och design
168
Kodkonvention
• Klasser påbörjas med stor bokstav (i övrigt små
utom om flera ord då Inledande bokstav i övriga ord
också görs stor)
– AClass
• Metoder/attribut inleds med liten bokstav (i övrigt
små utom om flera ord då inledande bokstav i övriga
ord också görs stor)
– aMethod(), aVariable
• Konstanter Bara stora bokstäver. Ord åtskiljs med _
– PI
• Följer man dessa konventioner så kommer ens egna
klasser att “se ut” som javas inbyggda.
169
Bra klasser är grunden
• Bra namn som säger vad den gör eller är till för
• Hanterar undantag och fel på ett bra sätt
• Väldokumenterad
–Vad den gör
–Hur dess relation är till andra klasser
–Publika metoder är dokumenterad
• Minimera möjligheten att ändra tillstånden
– använd setter och getters för att jobba med tillståndsförändring
– tänk efter om det behövs setters, eller fungerar det utan
• Undvik statiska attribut och metoder i så stor utsträckning det bara
går
170
Bra klasser är grunden
• Typer av klasser
•Actor-classes
•Utility-classes
•Oförändliga klasser,
• dvs man kan bara skapa objekt med värden och man har
inga setters för att ändra tillståndet -String är en sådan klass
• Inte för stora ansvar för en klass -> avspeglar sig i det publika
”interfacet” • Synligheten ligger på rätt nivå
• Minska beroendet till andra klasser
–Tex. undvik att lägga in metoder för utskrift i klasser försök att
lägga dom så högt upp i klasshierarkin som det bara går helst i
main-klassen.
•System.out.println finns inte i alla miljöer…
171
Några olika typer av klasser
•Actor-classes
•Gör saker med objekten, ändrar tillstånd, har ett
beteende, mm
•Utility-classes
•Ofta statiska klasser som inte jobbar med att
förändra objekts tillstånd,
–typ Math-klassen
•Oföränderliga klasser
•Klasser med ett fast tillstånd
–Man kan bara skapa objekt med värden och
man har inga setters för att ändra tillståndet 172
String är en sådan klass
Vad kännetecknar en god klass
• Med andra ord
• En odelad, väldefinierad abstraktion
• Coupling
• Uppgiften kan beskrivas kort och tydlig
• Namnet är en substantiv eller adjektiv som
beskriver abstraktionen på ett adekvat sätt
• Har ett koncist och sammanhängande
gränssnitt
•Cohesion
• Har tillstånd och beteende
173
Coupling
• Klasserna ska vara så oberoende som möjligt av
varandra
• Löst kopplade
• Coupling mäter hur starkt klasserna är kopplade
• Ju lösare klasserna är kopplade, desto
– enklare är det att förstå en enstaka klass
– enklare och förståeligare blir systemet som helhet
• Klasserna med lös koppling kan lättare ändras utan
att andra klasser påverkas
➨ Systemet blir lättare att ändra
➨ Dvs, Mera flexibilitet
174
UML: Klassrelationer
svag koppling
stark koppling
175
Klassdiagram
Johan Eliasson
Metoddesign
•Konsistens när det gäller parametrar och namn
–Följ en kodkonversion
•Undvik sidoeffekter i metoder om det går ,
•beräknar och returnera ett värde
•Undvik att förändra objekten som parametrarna
refererar till, returnera ett nytt objekt om det är ”görbart”
•Ibland använder man returvärdet för att säga något om
beräkningen hur den gick, trots att metoden inte returnerar
något riktigt värda
•Exceptions så långt det går
•jobba med undantag för att tala om att det har skett
något som anroparen behöver veta för att hantera ev.
177
konstigheter
Kategorier av metoder
• Konstruktorer
– Skapa instanser
• Selektor (get-metod)
– Returnerar information om objektets tillstånd
• Mutator (set-metod)
– Ändra objektets tillstånd
• Annat
– Gör någonting
➨ En metod ska tillhöra bara en kategori
178
Metoddesign
•Java jobbar med ”Call-By-Value” på attributen, dvs värdet från
anropssidan kopieras till attributen i metoden
•Undvik att förändra värdet på de anropade attributen,
speciellt när de är av referenstyp.
•Vad händer på anropssidan om du ändrar på parametern/
attributet inne i en metod
•använd final i attribut-listan för att göra det omöjligt att
ändra attributen inne i metoden
•Java kan inte förändra innehållet i variabeln som används för
anropet
•Java förändrar objektet som variabeln refererar till inne i en
metod
179
package callByValue;
import figures.Triangle;
public class CallByValue {
public static void main(String args[]){
int a = 42;
System.out.println("a before someMethod(a): " + a);
// a is copied and the copy is passed into the method
someMethod(a);
System.out.println("a after someMethod(a): " + a);
}
Triangle triangle = new Triangle();
System.out.println("triangle before triangleMethod(triangle): " + triangle);
// triangle ref is copied and the copy is passed into the method
triangleMethod(triangle);
System.out.println("triangle after triangleMethod(triangle): " + triangle);
public static void someMethod(int a){
a = 23;
System.out.println("a within someMethod(a) after changing it: " + a);
}
public static void triangleMethod(Triangle triangle){
triangle.changeSize(10, 400);
System.out.println("triangle within triangleMethod(triangle)
after changing it: " + triangle);
triangle = new Triangle();
System.out.println("a within triangleMethod(triangle)
after created a new triagle: " + triangle);
}
180
Cohesion
• Varje metod ska vara “ansvarig” för bara en
uppgift
• Cohesion mäter huruvida en metod uppfyller detta
krav
• Ju mer en metod fokuserar på en enda uppgift,
desto
– enklare är det att finna ett bra namn
– enklare och förståeligare blir koden
• Metoder med stark samhörighet kan lättare ändras
utan att andra metoder påverkas
➨ Det ska vara möjligt att beskriva en metod med en
enkel mening med ett verb och ett objekt
181
Cohesion: Exempel 1
• Exempel 1:
public void setNameAndAge (String name, int age);
Bättre:
public void setName (String name);
public void setAge (int age);
Exempel 2:
/* Anropas en gång om året */
public void calculateHolidays();
{
holidays += new Holidays();
age++;
}
Bättre:
public void calculateHolidays();
public void incrementAge();
182
Cohesion: Exempel 2
• Exempel 3:
public void setFirstName (String name){
firstName = name;
}
public void setLastName (String name){
lastName = name;
fullName = firstName + ”” + lastName;
}
Bättre:
public void setFirstName (String name) {
firstName = name;
fullName = firstName + ”” + lastName;
}
public void setLastName (String name){
lastName = name;
fullName = firstName + ”” + lastName;
}
183
F6
Filer och IO
184
Input/ output
Utströmmar från programmet
Inströmmar till programmet
185
Input/output
• Externa data kan tillhandahållas på många sätt i
Java :”
– Via GUI”
– Läsas och skrivas direkt till och från tangentbord och
bildskärm
– Läsas och skrivas till och från filer
– Läsas och skrivas till och från databaser
– …
• Java API tillhandahåller paketet java.io
– Klasser som stöder input/output”
– Klasser för att hantera när I/O-operationer går fel
• Typfel
• En fil som inte går att hitta/öppna
• …
186
Filer
• Filer kan användas för att lagra data
permanent mellan programkörningar eller för
utbyte an information mellan program.
• Textfiler läsbara även för oss (med tex en
texteditor) till skillnad från binära filer som
lagrar datat mer som i datorns minne.
187
Skriva och läsa textfiler
• Strömmarna betraktas som innehållande värden
av typen char
• Klasser som hanterar textfiler kallas readers och
writers
• Tre viktiga åtgärder
– Öppna filen
– Skriv/läs data
– Stäng filen
• Samtliga dessa kan misslyckas
– Man måste förutse undantag!
188
Öppna/Skriva/Stänga
• Skapa ett FileWriter objekt
• En konstruktor tar filnamnet som parameter
(String)
FileWriter writer = new
FileWriter(”testfil.txt”);
• Skriv med metoden write()
• Överlagrad, tex. med signaturen write(String
str)
writer.write("Första raden\n");
• Stäng filen med metoden close()
writer.close(); //(Obs stäng ej System.in)
189
Läsa från filer
• Scanner har en konstruktor som tar ett Fileobjekt som parameter
• Alternativt kan man koppla ihop den med
En Reader klass
•
File f=new File(“minfil.txt”);
•
Scanner in=new Scanner(f);
//Kastar ett kontrollerat undantag
•
Scanner in2=new Scanner(new
FileReader(“minfil.txt”));
•
String str=in2.next();
•
in2.close();
190
Exempel
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class CopyBytes {
public static void main(String[] args) throws IOException {
FileInputStream in = null;
FileOutputStream out = null;
try {
in = new FileInputStream("xanadu.txt");
out = new FileOutputStream("outagain.txt");
int c;
while ((c = in.read()) != -1) {
out.write(c);
}
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
191
}
Input/ output
• Java I/O baseras på streams (strömmar)
• En ström är en sekvens av bytes som flödar från en
källa till ett mål
• Fördefinierade standardströmmar:
ström
syfte
System.in
läsa input
System.out skriva output
System.err
skriva fel
enhet
tangentbord
bildskärm
bildskärm
datatyp
InputStream
PrintStream
PrintStream
• Standard strömmarna kan läggas om med
System.setIn/Out/Err metoderna
192
InputStream
• Inputströmmar är mera komplicerade
• InputStream är en abstrakt klass
• InputStream har väldigt begränsad mängd
metoder för att hantera inläsning
• System.in måste därför ofta ”kapslas in” i en
mer specifik ström med de egenskaper man vill ha
– Läsa in strängar
– Läsa in heltal
– Läsa in …
• Vi kommer titta på några olika klasser vi kan
använda till detta tex Scanner
193
Scanner
• Osmidigt att läsa in en hel rad som sträng! Bättre med
ett”ord” i taget.
• Vad är ett ”ord”?
• Tecknen måste grupperas till s.k. Tokens
• Tidigare mycket eget jobb
• Sedan Java 1.5 (5.0) finns klassen Scanner...
• Man kopplar en scanner till en ström
Scanner myScan = new Scanner(System.in);
• Finns metoder för att läsa in heltal, flyttal, ord, rader etc. Se
metoder i API:n
– https://docs.oracle.com/javase/7/docs/api/java/util/
Scanner.html
194
Koppla ihop strömmar
• För mer avancerat beteende kan de enkla strömmarna som
läser/skriver från någon kopplas ihop med mer avancerade
strömmar.
• Ex:
PrintWriter out=new PrintWriter(new
FileWriter(“filnamn.txt”));
out.println(“hej”);
out.close();
• Strömmar finns tex för buffring, hantera radnummer,
komprimering och kryptering
• https://docs.oracle.com/javase/7/docs/api/java/io/
PrintWriter.html
195
Exempel
import
import
import
import
import
java.io.FileReader;
java.io.BufferedReader;
java.io.IOException;
java.util.Scanner;
java.util.Locale;
public class ScanSum {
public static void main(String[] args) throws IOException {
Scanner s = null;
double sum = 0;
try {
s = new Scanner(new BufferedReader(new FileReader("usnumbers.txt")));
s.useLocale(Locale.US);
while (s.hasNext()) {
if (s.hasNextDouble()) {
sum += s.nextDouble();
} else {
s.next();
}
}
} finally {
s.close();
}
System.out.println(sum);
}
196
}
Object Serialization
• Object serialization är ett sätt att spara ett objekts
tillstånd (tex i en fil) så att vi sedan kan komma åt
det. Även efter att programmet avslutats.
• Object serialization åstadkoms mha
Serializable interfacet och klasserna
ObjectOutputStream och
ObjectInputStream
• writeObject metoden används för att
serializera ett objekt
• readObject används för att återskapa objektet
197
I/O Streams
Input Streams
Output Streams
Data
Streams
Processing
Streams
Character
Streams
Byte
Streams
•
•
•
•
•
•
•
Byte Streams handle I/O of raw binary data.
Character Streams handle I/O of character data, automatically handling translation to and
from the local character set.
Buffered Streams optimize input and output by reducing the number of calls to the native
API.
Scanning and Formatting allows a program to read and write formatted text.
I/O from the Command Line describes the Standard Streams and the Console object.
Data Streams handle binary I/O of primitive data type and String values.
Object Streams handle binary I/O of objects.
198