exempel_average_points.pdf

Ett exempel som illustration till Laboration 1
Filen java_average_points.zip innehåller Java-koden till exemplet.
Programmets avsikt enligt kravspecifikationen
Användaren matar in poäng på en tentamen för varje kursdeltagare, och programmet räknar ut och visar medelpoängen för tentamen.
Design
Programmets indelning i delsystem eller huvudfunktioner visas i denna modell (ingen formaliserad notation, går exempelvis bra med rektanglar i stället):
En fördjupning, förfining av denna modell visar hur bearbetningen är tänkt att gå till. Vi har en design i form av en algoritm. Den beskrivs i detta JSP-diagram:
Anvisningar om JSP: http://en.wikipedia.org/wiki/Jackson_structured_programming
Nedan visas hur samma bearbetning skulle kunna beskrivas med text.
1. Starta inmatningen för en kurs.
2. Skriv in poängen för en kursdeltagare, och upprepa detta för alla övriga kursdeltagare.
3. Tala om för programmet att det inte ska matas in fler.
4. Programmet summerar de inmatade poängen och räknar sedan ut medelpoängen.
5. Medelpoängen visas.
6. Programmet frågar om användaren om denne ska sluta eller registrera poäng för en annan kurs.
7. Om svaret är "sluta", avslutas programmet. I annat fall utförs steg 1.
I fortsättningen används endast JSP-diagrammet och den ovanstående beskrivningen av programmets avsikt.
1
Granskning 1
Granskning av designen
Antag, att detta JSP-diagram hade varit granskningsobjektet för deluppgift 1 i laborationen. Låt oss säga, att checklistan bara har tre kriterier (ID är ett identifikationsnummer för kriteriet där C:et indikerar att numret ett nummer i checklistan för att skilja det från andra nummer).
Checklista för design
ID
Fråga / kriterium
Kommentarer
C001
Är allt begripligt?
C002
Kommer algoritmen fram till en slutpunkt?
C003
Stämmer strukturen med den högnivåstrukturen?
C004
Finns logiska fel?
Kommentar: Skapa checklistan utifrån vad det är som ska granskas. Vissa kriterier kan användas i de flesta
checklistor, medan andra är specifika för den aktuella situationen.
Rada inte upp kriterier utan att ha tänkt igenom dem med avseende på överlappningar och vad du vill komma åt
med vart och ett av dem överlappningar. Är exempelvis C004 något som fångas upp redan av C001? Finns det
en skillnad mellan C001 och C004 – i så fall vilken är den?
Protokollet för den genomförda granskningen:
Protokoll för ganskning av JSP-diagrammet
Nr
Granskat
Felbeskrivning
KID
Defekttyp
1
Tomma rutan
Ska tomma rutan ha innehåll?
C001
1
2
Medelvärde
Varifrån kommer antalet som används för C001
att räkna ut medelpoängen?
2
3
JSP kommer att nu
ett slut
OK
C002
-
4
JSP, Högnivå
JSP-strukturen stämmer inte med indelningen i delsystem.
C003
2
5
Avbryt?,
Sluta?
Framgår inte vad som ska hända när
användaren har gjort sitt val
C003
2
Åtgärd
OK
X
Kommentarer till protokollet (här av pedagogiska skäl – i en verklig situation helst inga):
1. 2. Man begriper från sin allmänbildning, att en totalsumma ska divideras med antalet kursdeltagare för att få medelpoängen. Summan räknas ut i rutan Summera, men varifrån kommer antalet?
3. Kommer algoritmen till ett slut? Ja, det gör den. Det finns avslutningar på iterationerna och programmet.
4. JSP-diagrammets struktur stämmer inte med den övergripande modellens indelning i delsystem (eller huvudfunktioner). Exempel: Summering (en beräkning) utförs medan inmatningen av poängen pågår.
5. Det verkar vara underförstått, att om användaren svarar "Ja", blir variabeln avbryt = ”ja”, vilket leder till att iterationen upphör p.g.a. villkoret UNTIL avbryt. Motsvarande skulle då gälla för Sluta?.
När granskningen pågår, ska man inte gräva efter orsaker eller spekulerar om lösningar. Ett vanligt misstag är att falla
för frestelsen att göra detta. Håll under granskningen fokus på att enbart finna och notera olika slags defekter och
oklarheter.
2
Åtgärder för designen
Efter att granskningen är klar, analyseras resultatet och beslut fattas om vilka åtgärder som ska vidtagas. Det noteras
kortfattat i protokoll. Vid behov tillfogas kommentarer (av pedagogiska skäl finns här flera kommentarer).
Protokoll för ganskning av JSGP-diagrammet
Nr
Granskat
Felbeskrivning
KID
Defekttyp
Åtgärd
1
Tomma rutan
Ska tomma rutan ha innehåll?
C001
1
Gör jobbet
2
Medelvärde
Varifrån kommer antalet som används för att C001
räkna ut medelpoängen?
2
Ingen
3
JSP
OK
C002
-
-
4
JSP, Högnivå
JSP-strukturen stämmer inte med indelningen i delsystem.
C003
2
JSP ska följa högnivåstrukturen
5
Avbryt?
Sluta?
Framgår inte vad som ska hända när användaren har gjort sitt val
C003
2
Rita in.
OK
X
1. Den tomma rutan ska ha texten "Gör jobbet". Man kom inte på något bättre än så.
Ett alternativ var att ta bort rutan och låta rutorna "Inmatning", "Beräkning" och "Utskrift" anknyta till rutan "En
kurs". Den fick dock vara kvar p.g.a. att bearbetningen för en kurs kan ses som två huvudoperationer:
a. Utför allt som har att göra med att mata in poängen, beräkna medelpoängen och visa den. Detta skulle
kunna utföras av en metod (eller subrutin, procedur eller delsystem)
b. Fråga om användaren vill avsluta programmet.
Diagrammet skulle då kunna ses som fullständigt på den detaljnivån. Det som kommer under den tomma rutan är
en mer förfinad detaljnivå.
Utifrån den aktuella situationen bedömer designern hur mycket detaljer som behöver ritas. Diagrammet ritas uppifrån och ner - detaljnivå för detaljnivå. En ny detaljnivå läggs till där man finner behov av det.
2. Det finns inget behov av att i diagrammet visa varifrån antalet imatade poängtal hämtas, eftersom det anses vara
så självklart att det räknas ut på något sätt. Det överlåts därmed till programmeraren att lösa detta på ett lämpligt
sätt.
3. Ingen åtgärd. Om rutan "Sluta?" inte hade funnits, hade man varit tvungen att lägga till den.
4. Beslut fattades om att översiktsmodellens struktur med tre huvudfunktioner, ska framgå tydligt i JSP-diagrammet
och programkoden.
Att ändra strukturen, är en stor ändring med hög risk för att misstag begås och fel uppstår. Därför är det
vanligt, att denna typ av ändring undviks såvida det inte finns starka skäl till att genomföra den. Defekter rörande
strukturen måste alltså upptäckas och åtgärdas tidigt i utvecklingsarbetet, annars bör man vara beredd på att acceptera att de finns permanent i systemet. Att göra strukturella ändringar sent i utvecklingsarbetet, är mycket kostsamt, eftersom man måste riva upp mycket av det som redan har implementerats.
Här upptäcktes problemet tidigt. Det hade varit ännu bättre, om det hade skett innan någon programmering hade
hunnits utföras.
Man kan resonera så här om denna typ av defekt:
Är det viktigt att upprätthålla indelningen i tre huvudfunktioner? Algoritmen ser ju ut att ändå göra sitt jobb. Beställaren ser inte hur det går till på insidan utan endast att programmet visar rätt medelpoäng. Om beställaren
är nöjd med resultatet, varför då bekymra sig om systemets interna struktur?
Om beställaren tänker använda programmet en kort tid, spelar det ingen roll. Om beställaren tänker använda
det under många år som en viktig del i verksamheten, uppstår i framtiden behov av att ändra det och utöka
3
funktionaliteten, t.ex. att lagra poängen i en databas och göra statistiska beräkningar. Då underlättas underhållet och vidareutvecklingen, om systemet har en struktur med en tydlig indelning i funktioner.
5. Rita in i JSP-diagrammet: Vad som händer när användaren har gjort sitt val i rutorna Sluta och Avbryt. Detta görs
med tanke på att det senare under systemets livstid kommer att vara andra personer än utvecklarna, som utför
underhålls- och utbyggnadsarbetet.
Det visade sig vara fråga om en avsiktligt ofullständighet. Designern har gjort bedömningen, att det är självklart
vad som händer efter att användaren har svarat på frågan och ville då inte rita in det. Är det lika självklart, när någon annan än designern läser diagrammet? Om det inte är det, är det klokt att rita en ny detaljnivå. Man valde att
göra detta.
Korrigerat JSP-diagram
Rutan Avbryt! betyder att något ska göras så att UNTIL avbryt kommer att avbryta iterationen, t.ex. att en variabel med
namnet avbryt sätts till ”Ja”. Motsvarande för Sluta!. Om man uttryckligen vill ha en variabel för detta, kan det visas
genom att skriva avbryt = ja eller avbryt = true i rutan.
Ett streck i en ruta kallas för Null och betyder att inget ska göras. Det går inte att lägga till operationer under en Nullruta.
En förnyad granskning genomförs för att kontrollera att ändringar har genomförts. Om det är riktigt genomförda, markeras det med ett X i kolumnen OK.
4
Granskning 2
Granskning av hur programkoden uppfyller designen
Antag, att uppgift 2 i laborationen hade avsett nedanstående programkod. Det är meningen, att den ska utföra exakt
det som beskrivs i JSP-diagrammet.
package java_medelbetyg;
import java.io.IOException;
public class main {
public static void main(String[] args) throws IOException
{
//Använd main som ett "handtag" för att hålla ihop programmet.
//Själva programmet finns i övriga klasser.
//Klassen AveragePoints innehåller en metod som räknar ut medelvärdet
//av ett valfritt antal poängvärden, som användaren matar in.
//Det finns flera versioner av klassen, som förklaras i dokumentet
// exempel_average_points.pdf.
AveragePoints00 ap = new AveragePoints00();
//AveragePoints01 ap = new AveragePoints01();
//AveragePoints02 ap = new AveragePoints02();
//AveragePoints03 ap = new AveragePoints03();
//AveragePoints04 ap = new AveragePoints04();
//AveragePoints05 ap = new AveragePoints05();
//AveragePoints06 ap = new AveragePoints06();
ap.calculateAveragePoints(); // Rutan 'Medelpoäng för kurser' i JSP-diagrammet.
}
}
5
Klassens namn: AveragePoints00.
public void calculateAveragePoints() throws IOException {
// Utför rutan 'Medelpoäng för kurser' i JSP-diagrammet.
//Konstanter
final int ANTAL = 150; //Versaler för konstanter enligt konventionen för Java
//Variabler
int x, i = -1, sum; //x = talet som matades in, i = varvräknare, sum = summan av poängen.
String svar; //Användarens svar på frågor
//Designbeslut: Det ska få plats 150 tal. Två alternativ:
// 1. new int[ANTAL] ==> det första talet på index=0, det 150:e elementet har index 149 etc.
// 2. new int[ANTAL+1] ==> använd ej index 0, första talet på index = 1 och
//
det 150:e på index =150.
int[] tal = new int[ANTAL]; //Det 150:e elementet har index 149.
// För IO (denna programmerare ogillar scanner bl.a. pga problemen när olika datatyper ska
// läsas). BufferedReader är entydig och tillförlitlig.
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
do { // En kurs
// Tomma rutan i JSP-diagrammet utgörs av allt inne i loopen
do { //Inmatning (användaren matar in poängen ett åt gången)
System.out.printf("Poäng följd av ENTER (0 och ENTER för att avsluta): ");
i = i + 1;
x = Integer.parseInt( br.readLine() ); //Inmatning en poäng
tal[i] = x;
// Programmeraren använder talet 0 för att representera att användaren vill
// avbryta inmatningen.
} while ( !(x == 0) || i == ANTAL ); // Avbryt?
//Programmeraren vet att det inte går att lägga in fler värden än vad arrayen är
//dimensionerad för och såg till att loopen avbryts om arrayen är full ( i === ANTAL).
//Ett tillägg i koden av något som inte finns i designen (JSP-diagrammet).
//Beräkning
//Summera
sum = 0;
for (int k = 0; k <= i; k++) {
sum = sum + tal[k];
}
//Medelvärde.
// 7 positioner för decimaltalet, varav 1 decimal (medför standardavrundning),
// högerjusterat.
// För att få decimalresultat av division av två integer:
// konvertera ett av talen till double.
System.out.print("Medelpoäng: ");
System.out.format("%7.1f%n", (double)(sum/(i + 1)));
//Sluta?
System.out.printf ("Sluta? (J/N) ");
svar = br.readLine();
} while (!svar.equals("J")); //Sluta?
System.out.println("Klart.");
} //END calculateAveragePoints00
6
Checklista för programkodens överensstämmelse med designen
ID
Fråga / kriterium
Kommentarer
C001
Går det att följa från diagrammet till koden?
Spårbarhet är mycket viktigt.
C002
Stämmer koden med diagrammet?
Den kan göra det även om spårbarheten är dålig.
C003
Svårt att förstå vad koden gör?
En svårbegriplig kod medför höga underhållskostnader.
C004
Finns logiska fel?
Leder ofta till felaktigt beteende. En allvarlig typ av
defekt.
Kommentarer:
 C002 kan delas upp i flera kriterier, t.ex. alla brytvillkor för loopar, selektioner.
 Även kriterier för god programmerarsed och god programstruktur kan beaktas. Sök på Internet för coding conventions, programming practice och liknande begrepp. Exempel på två webbplatser för detta:
 http://en.wikipedia.org/wiki/Coding_conventions
 http://en.wikipedia.org/wiki/Best_coding_practices
Protokoll för ganskning av programkodens överensstämmelse med designen (ingen testkörning)
Nr
Granskat
Felbeskrivning
KID
Defekttyp
Åtgärd
OK
1
Spårbarhet design till
kod
OK pga samma namn i nästan
samma struktur
C001
-
X
2
Rutan Avbryt?
OK
C002
-
X
3
Separeringen av Beräkning och Utskrift.
Uträkning och utskrift av medelvärdet C002
ej separata i koden.
2
4
Hur antalet inmatade
poäng fås fram
Svårt att förstå vad (i + 1) är i denna
programrad:
C003
2
System.out.format("%7.1f%n",
(double)sum/(i + 1))
5
Alla möjliga poängtal
går att mata in
Går inte att ange 0 poäng pga att
nollan tolkas som att användaren vill
avbryta inmatningen.
C004
3
6
Rutan Avbryt?
Ej i JSP: Koden avbryter automatiskt
när man har mata in det maximala
antalet poäng som ryms i arrayen.
C002
2
7
Brytvillkoret för iterationen Inmatning
Ingen variabel som heter ’avbryt’ trots C003
att JSP tyder på att den borde finnas
2
8
Brytvillkoret för iterat- Ingen variabel som heter ’sluta’ trots C003 2
ionen En kurs
att JSP tyder på att den borde finnas
Kommentar: För programkod kan det bli lättare att hänvisa till vissa platser i koden, om man visar radnumren (felbeskrivningen i punkt 4 hade blivit kortare då).
1. Ja, det går. Det finns kommentarer i koden där namnen i JSP-diagrammet används (borde kanske markeras tydligare?). Det gör det lättare att hitta platsen i koden, där det som hör till rutan utförs.
Brytvillkoren för rutorna En kurs och Inmatning visas i koden med en kommentar på rätt plats.
2. Vid inmatningen av en poäng skriver användaren in talet noll, vilket tolkas som en order om att avbryta. Rutan
Avbryt? säger egentligen bara att programmet ska kontrollera, om det har hänt något, som gör att inmatningen av
poäng ska avbrytas. Det sägs inget om hur det ska göras. Programmeraren har löst det på detaljnivå som inte
finns i JSP-diagrammet. Koden stämmer med diagrammet.
3. I programkoden görs uträkningen av medelvärdet och utskriften av det i en enda instruktion:
7
System.out.format("%7.1f%n", (double)sum/(i + 1)).
Det ska inte vara så enligt diagrammet: Först räknas medelvärdet ut, och sedan skrivs det ut. Det är två olika operationer. Notera i protokollet: ”Uträkning och utskrift av medelvärdet ej separata i koden.”
Hur ska denna avvikelse klassas? För användarens del fungerar den nuvarande lösningen. Det skulle kunna motivera klassning som typ 1 eller rent av som inget fel alls. Lösningen försvårar däremot systemunderhållet, eftersom den inte följer huvudstrukturen, som är utformad för att underlätta underhållet och den framtida utbyggnaden genom att ha en tydlig struktur utifrån en indelningen i huvudfunktioner. Då måste avvikelsen klassas som allvarligare en typ 1. Det blir då typ 2, eftersom det inte är tillräckligt allvarligt för att klassas som typ 3.
4. Det är nödvändigt att sätta sig in i den föregående programkoden för att förstå att (i + 1) är antalet imatade poängtal. Om det är svårt att förstå vad programmet gör, tar det längre tid att utföra underhållsarbete, vilket därmed kostar mer pengar. Dessutom ökar risken för att nya fel uppstår p.g.a. oförutsedda sidoeffekter (ripple effects).
5. Fritt från logiska fel?
Inmatningen av poängen 0 tolkas som att användaren vill avbryta inmatningen av poäng. Det gör det omöjligt att
registrera att en kursdeltagare har fått noll poäng. Detta är ett logiskt fel. I protokollet noteras ”Går ej att registrera
0 poäng”. Att programmet inte kan göra allt som det ska kunna, är ett allvarligt fel.
6. Designern har inte tänkt i termer av att det skulle finnas ett maximalt antal. Detta framtvingas genom att programmeraren har valt en array för att lagra de inmatade poängen. Om de i stället hade lagrats i en lista, skulle det inte
ha funnits något maximalt antal.
7. Koden tar längre tid att förstå, om det inte är tydligt vad det är som avgör om loopen ska avbrytas. I koden finns
dessutom två brytvillkor, vilket gör det värre. Undvik att ha en massa jämförelser while-klausulen. Det blir komplicerat att göra rätt om det finns flera AND och OR i jämförelsen.
Också fortsätter man så tills man har kontrollerat allt som är av intresse för att kunna avgöra hur pass väl som programkoden representerar lösningen i designen. För att upprepa, denna granskning hade som syfte att kontrollera att
programkoden stämmer med designen, och därför gjordes ingen testexekvering av den.
Programmerare har förstås gjort sina egna exekveringstester för att försäkra sig om att allt blir rätt, t.ex. att programmet har ett korrekt värde på hur många poängtal som har matats in.
Innan man går vidare med systematiska, protokollförda testkörningar, vill utvecklingsgruppen försäkra sig om att programmet har en struktur och utför operationer, som stämmer med designen. Resultatet av denna jämförelse kan göra
det nödvändigt att ändra programkoden och/eller designen (programmeraren kan ha gjort en bättre lösning).
8
Åtgärder för programkoden
Protokoll för ganskning av programkoden jämförd med designen (ingen testkörning)
Nr Granskat
Felbeskrivning
KID
Defekt- Åtgärd
typ
1
Spårbarhet design till
kod
OK pga samma namn i nästan samma
struktur
C001 -
Ingen
X
2
Rutan Avbryt?
OK
C002 -
Ingen
X
3
Separeringen av Beräkning och Utskrift.
Uträkning och utskrift av medelvärdet ej
separata i koden.
C002 2
Separera
4
Hur antalet inmatade
poäng fås fram
Svårt att förstå vad (i + 1) är på program- C003 2
raden System.out.format...
Ny variabel för
antal inmatade
poäng.
5
Alla möjlig poängtal
går att mata in
Går inte att ange 0 poäng pga att nollan
tolkas som att användaren vill avbryta
inmatningen.
Annat sätt att
avbryta
6
Rutan Avbryt?
Koden avbryter automatiskt när man har C002 2
mata in det maximala antalet poäng som
ryms i arrayen.
Ingen
7
Brytvillkoret för iterationen Inmatning
Ingen variabel som heter ’avbryt’ trots att C003 2
JSP tyder på att den borde finnas
Ny variabel:
avbryt
8
Brytvillkoret för iterationen En kurs
Ingen variabel som heter ’sluta’ trots att
JSP tyder på att den borde finnas
Ny variabel:
sluta
C004 3
C003 2
OK
X
Kommentar: För programkod kan det bli lättare att hänvisa till vissa platser i koden, om man använder radnumren.
Efter att granskningen är klar, analyseras resultatet och beslut fattas om vilka åtgärder som ska vidtagas.
3. Uträkningen av medelvärdet och utskriften av det ska göras om till separata operationer i programkoden.
4. Det blir tydligt vad som är detta värde och hur det fås fram. Det finns olika sätt att göra detta, t.ex. en räknare i
loopen eller summeringen i +1 efter loopen.
5. Använd något annat än ett tal för att tala om att inmatningen av poäng ska avbrytas.
6. Defektens orsak är att programmeraren har valt en array för att lagra poängtalen. Lösningen med en array fungerar. Den behålls, eftersom det skulle medföra alltför stort arbete med att göra om programmeringen för att använda en annan datastruktur.
Om man i arbetet med kravspecifikationen hade frågat sig hur många poängtal, som ska kunna hanteras i en kurs,
hade man antingen preciserat ett högsta antal eller lämnat öppet för ett obegränsat antal. JSP-diagrammet hade
medvetet kunnat ritas så att detta framgick.
Rutan Avbryt? definieras som att den avser icke specifierade kontroller av sådant, som ska leda till att inmatningen av poäng avbryts. JSP-diagrammet bedömdes bli svårtläst, om detaljer om alla anledningar ritades in. Det
skulle också bli besvärligt att uppdatera det, om man senare kom fler anledningar till att inmatningen ska avbrytas.
Beslut fattades om att detaljer om kontrollen av anledningarna till att avbryta ska finnas samlade tillsammans i
programkoden. Om det finns många och komplicerade villkor, kan en särskild metod få hantera detta.
Detta problem antyder, att det kan uppstå besvärliga situationer, om an börjar programmera innan designen och
kravspecifikationen är tillräckligt genomtänkta. Det bör ha gjorts minst två granskningar innan programmeringen påbörjas: en som upptäcker defekter och en som bekräftar att alla defekterna har åtgärdats.
7. Lägg jämförelserna inne i loopen i stället för att ha dem på while-raden. En boolesk variabel används för att minnas resultatet av jämförelserna, och det är bara den som finns på while-raden.
Lägg märke till att flera av ändringarna avser att göra programmets operationer och struktur lätta att förstå, vilket är
detsamma som att underlätta underhållsarbetet.
9
Korrigerad programkod
Klassens namn: AveragePoints01.
//ÄNDRAT FRÅN VERSION 00:
// Lagt till antalInmatade.
// x och sum har nu datatypen Double - ändrat på flera platser
// Ändra brytvillkor i inmatningsloppen från 0 till -1.
// Medelpoängen räknas ut och skrivs ut som två separata operationer.
public void calculateAveragePoints() throws IOException {
// Utför rutan 'Medelpoäng för kurser' i JSP-diagrammet.
//Konstanter
final int ANTAL = 150; //Versaler för konstanter enligt konventionen för Java
//Variabler
int i = -1; //i = varvräknare, 1 lägre än det lägsta värdet som arrayindexet får ha
Double x, sum; //x = talet som matades in, sum = summan av poängen. Double för decimalpoäng
//och för att få decimaler i resulatet av divisionen.
int antalInmatade; //NYTT: Används för tydlighet för den som läser koden
String svar; //Användarens svar på frågor
Double medel; //Medelvärdet
Boolean avbryt, sluta; //NYTT: För tydlighet i looparnas brytvillkor
//Designbeslut: Det ska få plats 150 tal. Två alternativ:
// 1. new int[ANTAL] ==> det första talet på index=0, det 150:e elementet har index 149 etc.
// 2. new int[ANTAL+1] ==> använd ej index 0, första talet på index = 1 och
//
det 150:e på index =150.
Double[] tal = new Double[ANTAL]; //Det 150:e elementet har index 149.
//IO (denna programmerare ogillar scanner bl.a. pga problemen när olika datatyper ska
//läsas). BufferedReader är entydig och tillförlitlig.
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// 'Gör jobbet'-rutan i JSP-diagrammet utgörs av allt inne i loopen
do { // En kurs
//Inmatning (användaren matar in poängen ett åt gången)
do { //Inmatning en poäng (den tomma rutan i JSP-diagrammet)
System.out.printf("Poäng följd av ENTER (-1 för att avsluta): ");
i = i + 1;
x = Double.parseDouble( br.readLine() ); //Användaren skriver in ett tal
tal[i] = x;
// Programmeraren använder talet -1 för att representera att användaren vill
// avbryta inmatningen.
//
Om x är -1 sätts (x == -1) True, annars till False.
//
Om i = ANTAL, sätts i == ANTAL till True,annars till false.
//
Om det är minst ett True, sätts avbryt till True, annars till False.
avbryt = (x == -1) || i == ANTAL;
} while ( !avbryt); // Avbryt?
//Programmeraren vet att det inte går att lägga in fler värden än vad arrayen är
//dimensionerad för och såg till att loopen avbryts om arrayen är full ( i === ANTAL).
//Ett tillägg i koden av något som inte finns i designen (JSP-diagrammet).
antalInmatade = i + 1;
// Beräkning
10
//Summera
sum = 0.0;
for (int k = 0; k <= i; k++) {
sum = sum + tal[k];
}
//Medelvärde.
// 7 positioner för decimaltalet, varav 1 decimal (medför standardavrundning),
// högerjusterat.
// För att få decimalresultat av division av två integer:
// konvertera ett av talen till double.
System.out.print("Medelpoäng: ");
medel = (double)(sum/antalInmatade);
// Utskrift
System.out.format("%7.1f%n", medel );
//Sluta?
System.out.printf ("Sluta? (J/N) ");
svar = br.readLine();
sluta = svar.equals("J");
} while (!sluta); //Sluta?
System.out.println("Klart.");
} //END calculateAveragePoints
Det finns ett annat sätt att skriva denna rad, om den upplevs som svår att förstå (skulle kunna kallas smartkodning):
avbryt = (x == -1) || i == ANTAL;
Så här:
avbryt = false;
IF (x == -1) { avbryt = true; }
IF (i == ANTAL) {avbryt = true; }
//
//
//
//
Ge variabeln ett default-värde.
Ersätt default värdet
Ersätt default värdet även om det redan gjorts
på raden ovanför (slipper använda else).
Detta är ett vanligt sätt att skriva för att slippa komplicerade sektionsstrukturer, som t.ex. denna enkla variant:
if (x == -1) { avbryt = true; }
else if (i == ANTAL) {avbryt = true; }
else {avbryt = false; }
Dessa tre kodstycken utför samma sak, men på olika sätt. De är ekvivalenta.
Vilket av dessa tre sätt gör det lättast för dig att förstå vad koden gör? Använd det!
Lägg märke till att detta
avbryt = (x == -1) || i == ANTAL;
} while ( !avbryt); // Avbryt?
gör det lättare att förstå när While ska avbrytas, än detta:
} while ( !(x == 0) || i == ANTAL ); // Avbryt?
11
Granskning 3
Exekvering av programkoden med avsikten att hitta så många fel som möjligt.
Inspektion av programkodens kvalitet
Man granskar programkoden för att se om logiken är korrekt. Även kriterier för god programmerarsed och god programstruktur kan beaktas (sök på Internet för coding conventions, programming practice och liknande begrepp).
1. Räknas medelvärdet ut korrekt?
Granskaren gör en skrivbordskörning av detta segment av koden. Variabelvärden skrivs in på pappret.
Granskaren bestämde sig för att se vilket resultat koden kommer att producera, om man matar in poängen 2 och
3. Kolumnerna 1, 2 och 3 innehåller variabelvärdena för varv 1, 2 och 3 genom DO-loopen. Nere till höger finns
fyra kolumner för värdena 0, 1, 2 och 3 på variabeln k. Under var och en finns värdena för sum + tal[k]. Det visar
sig, att resultatet blir 1,33. Det är fel, eftersom det rätta är 2,5.
Man kan förstås även provköra koden och upprätta ett testprotokoll:
Test Funktion
Input
Förväntad
2,5
1
Korrekt uträkning av medelvärde 2 3 -1
…
(Den blå texten är vad som kallas för ett testfall.)
Output
Verklig
1,33
FEL!
Alltså är något fel med hur uträkningen går till. I detta läge behövs ingen analys och felsökning. På inspektionsmötet meddelar inspektören de andra om problemet, vilket noteras i protokollet: ”Medelvärdet räknas ut fel”.
När granskningen är klar, diskuteras de upptäckta problemen. När det gäller detta problem, ges en av personer i uppdrag att analysera orsaken till felet.
12
Åtgärder
Ska denna granskare även skriva om koden så att uträkningen görs korrekt? Det beror sig på om det finns andra ändringar, som berör samma avsnitt i koden. Det är inte bra om olika personer sitter på var sitt håll och gör sina ändringar.
Det behövs samordning, t.ex. något av dessa alternativ:

Först gör den ena personen sina ändringar. Detta är de viktigaste ändringarna eller de som leder till störst förändringar av kodstrukturen. I detta fall är det utan tvekan viktigast att först se till att medelpoängen räknas ut korrekt.
Därefter gör den andra person sina ändringar och får då rätta sig efter den korrigerade kodens utseende.

Samma person gör alla ändringar som berör samma avsnitt av koden.
Analys av felorsaken:
Varvräknaren i räknas upp under det varv då användaren väljer att avbryta och visar därmed en inmatning för mycket
(i får ett för högt värde). Man bestämmer sig för att ändra koden så att variabeln i endast räknas upp, om användaren
inte har matat in -1. Samma gäller för instoppningen av värdet i arrayen.
Koden inne i loopen blev så här efter korrigeringen:
Klassens namn: AveragePoints02.
do {
System.out.printf("Poäng följd av ENTER (-1 för att avsluta): ");
x = Integer.parseInt( br.readLine() );
avbryt = (x == -1);
if ( !avbryt ) {
i = i + 1;
tal[i] = x;
// Undvik att ändra ’avbryt’ från True till False. Ändra bara om det är till True.
if (i == ANTAL){ avbryt = true; }
}
} while ( !avbryt );
Den gamla metoden kopierades och gavs namnet calculateAveragePoints2, och koden i denna kopia skrevs om.
Du kan i stället byta namn på dem gamla och låta den omskrivna fortfarande heta calculateAveragePoints.
En skrivbordskörning visar i samma stil som föregående kodexempel visar, att nu blir medelpoängen den rätta.
Konstruera testfall
Nu ska man skriva ett testfall, som demonstrerar, att denna uträkning alltid hanteras rätt av programmet.
Först en black box-test. Man matar in vissa poängvärden och kontrollerar att den uträknade medelpoängen är rätt.
Test Funktion
Input
Förväntad
Output
Verklig
1
Korrekt uträkning av medelvärde 2 3 0
2,5
1,33
…
Efter korrigering av programkoden: Kod version 02 (eller hur du föredrar att göra numreringen)
1
Korrekt uträkning av medelvärde 2 3 -1
2,5
2,5
Sluta=N
2
Korrekt uträkning av medelvärde 8 5 10 -1
7,7
5,6
(körs direkt efter Test 1)
(7,666 avrundat)
13
FEL!
FEL!
En mer noggrann skrivbordskörning, som innefattar mer av koden, skulle avslöja felorsaken. För exemplets skull bestämmer sig testaren för se vilka värden som sum och antalInmatade har. Den rödmarkerade raden läggs till och
programmet exekveras en gång till med samma input (avkommentera i koden, om du vill provköra detta). Man utför nu
white-box-testning.
//Summera
sum = 0;
for (int k = 0; k < antalInmatade; k++) {
sum = sum + tal[k];
}
System.out. println(”sum=” + sum + ” antal=” + antalInmatade);
//Medelvärde.
...
Utskriften under exekveringen ger sum=28 och antal=5. Varken summan eller antalet är rätt. De ska vara 23 respektive 3.
En analys visar, att varvräknaren i inte nollas, innan värdena för Test 2 matas in. När talet 8 matas in, kommer i att få
värdet 3, och talet 8 kommer att läggas in på index 2 (tredje elementet) i arrayen. Värdena från Test 1 ligger alltså kvar
och kommer med i Test2.
Åtgärd: Ändra koden så att i sätts till -1 innan inmatnings loopen börjar. Initier inte till -1 i variabeldeklarationen.
Klassens namn: AveragePoints03.
//Variabler
int i; //i = varvräknare
…
do { // En kurs
//Inmatning (användaren matar in poängen ett åt gången)
i= -1; // i = 1 lägre än det lägsta värde som arrayindexet får ha
do { //Inmatning en poäng
…
Testkörning igen:
Test Funktion
Input
Förväntad
Output
Verklig
1
Korrekt uträkning av medelvärde 2 3 0
2,5
…
Efter korrigering av programkoden: Kod version 02 (eller hur du föredrar att göra numreringen)
1
Korrekt uträkning av medelvärde 2 3 0 Sluta=N
2,5
2
Korrekt uträkning av medelvärde 8 5 10
7,7
(körs direkt efter Test 1)
(7,666 avrundat)
Efter korrigering av programkoden: Kod version 03
1
Korrekt uträkning av medelvärde 2 3 -1 Sluta=N
2,5
2
Korrekt uträkning av medelvärde 8 5 10 -1
7,7
(körs direkt efter Test 1)
(7,666 avrundat)
Då var detta felet korrigerat.
14
1,7
FEL!
2,5
5,6
FEL!
2,5
7,7
2. Avbryts loopen för Inmatning korrekt, när arrayen innehåller det maximalla antalet element?
Konstruera ett testfall, som kontrollerar att inmatningen avbryts, om antalet inmatade poäng överstiger 150. För att
slippa mata in så många värden sätts ANTAL = 3 (testaren måste komma ihåg att ändra till 150 när testningen är
klar). Testfallet kontrollerar att tre värden accepteras, men inte fyra.
Test Funktion
Input
Förväntad
Output
Verklig
…
7
1 2 3 -1
2,0
2,0
1 2 3 4 -1
Värdet 4 ska inte
accepteras.
Programmet kraschar med
meddelande om att
ArrayIndexOutOfBoundaries.
8
Maxantalet inmatningar upprätthålls.
Ändra ANTAL till 3.
Maxantalet inmatningar upprätthålls.
Ändra ANTAL till 3.
Felmeddelandet talar om vilket rad, som felet inträffade på:
Programkoden reagerar tydligen inte som den ska här. Felmeddelande säger, att koden försöker använda ett indexvärde, som ligger utför arrayens tillåtna indexgränser. Det är precis det som händer, om det fjärde värdet accepteras
och sedan ska stoppas in som fjärde element i en array som har deklarerats för tre.
15
Koden skrivs om så att talet stoppas in i arrayen endast om det får plats:
Klassens namn: AveragePoints04.
do {
System.out.printf("Poäng följd av ENTER (-1 för att avsluta): ");
x = Integer.parseInt( br.readLine() );
avbryt = (x == -1);
if ( !avbryt ) {
i = i + 1;
// Undvik att ändra ’avbryt’ från True till False. Ändra bara om det är till True.
if (i == ANTAL){ avbryt = true; }
else { tal[i] = x; } //Endast om x får plats i arrayen
}
} while ( !avbryt );
Ny testkörning:
Programmet försöker fortfarande använda indexvärden utanför arrayens indexgränser, men på en annan plats i koden
än förra gången.
Orsaken till felet är, att i tillåts bli större än det högsta antalet tillåtna element i arrayen (i detta fall 2). Efter att i har
blivit 2 på rad 50, ska loopen avbrytas på rad 55, men det sker inte.
16
Koden korrigeras så att följande händer, när i har blivit 2: x lagras i arrayen på index 2, avbryts sätts till True, loppen
avbryts.
Klassens namn: AveragePoints05.
do {
System.out.printf("Poäng följd av ENTER (-1 för att avsluta): ");
x = Integer.parseInt( br.readLine() );
avbryt = (x == -1);
if ( !avbryt ) {
i = i + 1;
tal[i] = x;
// Undvik att ändra ’avbryt’ från True till False. Ändra bara om det är till True.
if (i == ANTAL - 1){ avbryt = true; }
}
} while ( !avbryt );
Kanske utvecklaren borde överväga att lägga till konstanten talMaxIndex = ANTAL – 1 och döpa om variabeln
i till talAktuelltIndex och skriva så här i koden: if (talAktuelltIndex == talMaxIndex) ...
Det skulle göra det tydligare vad det är som kontrolleras i villkoret.
Exekveringen ger nu ett korrekt beteende:
Direkt efter den tredje inmatningen räknas medelvärdet ut. Den kombinerade testaren och programutvecklaren anser,
att detta är ett acceptabelt beteende, men överlåter åt utvecklingsgruppen att fatta det slutliga beslutet om det.
Så här håller man på tills allting fungerar till belåtenhet.
Kommentar:
I den urspungliga koden i klassen AveragePoints05 gjordes jämförelse så här:
while ( !avbryt || i == ANTAL )
För att allt ska fungera korrekta måste uttrycket skrivas om till detta:
while ( !(x == -1) && !(i == ANTAL - 1) )
Tips: Uttryck som innehåller flera jämförelser med negationer samt AND eller OR är vanliga felkällor. Var alltid misstänksam mot dem, och acceptera dem inte förr de har testats och analyserats noga.
17
Klassens namn: AveragePoints00.
public void calculateAveragePoints() throws IOException {
final int ANTAL = 150;
int x, i = -1, sum;
String svar;
int[] tal = new int[ANTAL]; //Det 150:e elementet har index 149.
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
do { // En kurs
do { //Inmatning
System.out.printf("Poäng följd av ENTER (0 och ENTER för att avsluta): ");
i = i + 1;
x = Integer.parseInt( br.readLine() ); //Inmatning en poäng
tal[i] = x;
} while ( !(x == 0) || i == ANTAL ); // Avbryt?
sum = 0;
for (int k = 0; k <= i; k++) {
sum = sum + tal[k];}
System.out.print("Medelpoäng: ");
System.out.format("%7.1f%n", (double)(sum/(i + 1)));
System.out.printf ("Sluta? (J/N) ");
svar = br.readLine();
} while (!svar.equals("J")); //Sluta?
System.out.println("Klart.");
}
18