Göm menyn

Kvalitetskriterier

Vad är syftet med kvalitetskriterierna?

Att ni ska tvingas följa en stelbent kodningsstandard? Nej, givetvis inte, och vi har inte heller krav på specifik indentering eller placering av {} i koden. Det kan utvecklingsmiljön fixa åt oss.

Den här sidan ska istället vara en hjälp till er när ni arbetar på att lära er vad man behöver veta för att skriva högkvalitativ kod. Kod som ni kan vara stolta över, som ni lättare kan utöka och ändra, som ni kan förstå ett år senare, och som andra kan förstå om de skulle ta över ett projekt efter er.

Sidan är inte uttömmande: Det finns fler aspekter av kvalitet än de som vi diskuterar här. Men allt som står här är sådant som vi tittar på när vi bedömer kod i labb 4 och till ännu högre grad i projektet. Kod som inte uppfyller kriterierna kommer att returneras för komplettering. Så gör vi även om det är uppenbart hur man skulle fixa koden enligt kriterierna – för det är genom att göra man lär sig mest, inte genom att förklara hur man kunde ha gjort.

Kriterierna är alltså ett inlärningstillfälle, och ett steg på vägen till att skriva bättre programkod och bli en bättre programmerare!

Läs detta i förväg, kontrollera att ni följer kriterierna under programmeringens gång, och dubbelkolla inför varje inlämning. Tänk även på allt det vi diskuterar angående bra kodkvalitet på föreläsningarna.

Läsbarhet och dokumentation

Motivering

Programkod måste vara lättläst och väldokumenterad.

En anledning är att man ofta spenderar mycket tid på kod som skrivits tidigare: Fixa buggar, addera ny funktionalitet, förstå hur en metod ska anropas för att kunna använda den i en annan del av programmet, osv. Tiden som läggs ner på att göra kod lättläst och att dokumentera är därför inte bortslösad utan sparas in med råge i senare faser av utvecklingen.

Namngivning

Namngivning är mycket viktigt för läsbarhet.

  • Ge namn till värden. Namngivna konstanter (t.ex. de som deklareras som f inal static int) eller enum-typer är mycket bättre än "magiska konstanter" i koden. Det är lättare att förstå "if (state == State.RUNNING)" än "if (state == 14)", och det är lättare att förstå "xpos + blockSize" än "xpos + 42".

  • Använd informativa och rättvisande namn. En metod som är tänkt att flytta alla fiender i ett spel kan gärna heta moveEnemies(), medan update() visserligen stämmer men inte är så informativt. En lista som lagrar alla uppkopplingar i en FTP-klient kan heta connections eller kanske currentConnections, medan c inte är informativt och windows är missvisande, även om varje uppkoppling har ett eget fönster på skärmen.

  • Ge enhetliga och konsekventa namn. Blanda till exempel inte språk.

  • Följ namngivningsstandarden. Java har en vedertagen namngivningsstandard som ska följas. Detta hjälper Java-programmerare att snabbare förstå varandras kod. Standarden har diskuterats under föreläsningarna.

Namngivning före dokumentation

I många fall kan bra namngivning minska behovet av dokumentation.

  • Exempel 1:
    int i = 10; // Number of pixels to move for each step

    Förbättrad namngivning (som också gör det lättare att förstå vad som händer på alla ställen som faktiskt använder variabeln):
    int pixelsPerStep = 10;

  • Exempel 2: Blanda en kortlek
    // Shuffle the deck (standard decks have 52 cards)
    for (int i = 1; i <= 52; i++) {
        // 53 is the deck size + 1
        deck.swap(i, i + randomInt(53-i) - 1);
    }

    Med en variabel till blir 52/53 självdokumenterande:
    final int deckSize = 52;
    // Shuffle the deck
    for (int i = 1; i <= deckSize; i++) {
        deck.swap(i, i + randomInt(decksize+1-i) - 1;
    }

    Med en separat metod har man givit ett namn till vad loopen gör (detta ska så klart göras med viss måtta):
    final int deckSize = 52;
    shuffle(deck, deckSize);
    ...
    private void shuffle(Deck deck, int deckSize) {
        for (int i = 1; i <= deckSize; i++) {
            deck.swap(i, i + randomInt(decksize+1-i) - 1;
        }
    }

Dokumentation

Namngivning hjälper, men ofta behövs också kommentarer och annan dokumentation. Tänk på att handledaren och examinatorn ska läsa genom hela projektet!

  • Övergripande dokumentation som förklarar programmets struktur är ofta det viktigaste. Detta kan med fördel skrivas i ett separat dokument eftersom man ofta behöver läsa den för att överhuvudtaget veta vilka klasser man är intresserad av, om den ens är relaterad till en specifik klass. För projektet finns det plats för detta i projektbeskrivningen.

    Ett par förkortade exempel på vad som kan passa in här: "Alla rörliga objekt på skärmen är subklasser till Mover. Vill man ha flygfunktionalitet kan man ärva från Flier istället, eftersom detta har inbyggda funktioner för att landa. Varje rörligt objekt har en egen bakgrundstråd och rör sig individuellt med synkronisering via... /alternativt/ Vi använder en centraliserad spelloop som med jämna mellanrum anropar samtliga rörliga objekts uppdateringsmetod så de kan röra sig ett steg. Objekt som vill uppdateras mer sällan kan ange detta genom..."

  • Skriv också en beskrivande Javadoc-kommentar för varje klass. Javadoc kan även behövas för många fält eller metoder, men i vissa fall (t.ex. enkla getters/setters) är koden i sig själv så tydlig att kommentarer inte behövs.

    Javadoc-formatet beskrivs i en kursbok du har valt, eller t.ex. i Oracles egen dokumentation.

  • I övrigt: Istället för att omedelbart kommentera, försök först att skriva koden så dess betydelse är uppenbar. Detta är så klart ingen universallösning, men bra kod kan faktiskt relativt ofta göra kommentarer överflödiga. Då slår man två flugor i en smäll eftersom detta gör även övrig kod mer lättläst – och man minskar risken att man glömmer uppdatera en kommentar när koden ändras.

    I vissa fall kan det vara mer eller mindre oundvikligt att koden är icke uppenbar eller potentiellt svår att förstå, även efter rimliga förenklingar. Sådan kod måste också dokumenteras.

  • Det som behöver dokumenteras ska dokumenteras lagom mycket och på rätt nivå. Skriv t.ex. gärna kodkommentarer som förklarar varför koden gör något, när det inte är uppenbart. Denna typ av kommentarer är ofta svårare att ersätta med lättläst kod.

    Ett vanligt fel är att man istället kommenterar steg för steg vad koden gör. Detta är oftast redundant eftersom man lätt kan förstå det själv. Överdrivet exempel:

      windows.add(myNewWindow); // Add the window to the list

Struktur för klasser och paket

Strukturera klasserna enligt standarden:

Använd paket

  • Alla klasser ska ligga i ett namngivet paket!

  • Dela upp i flera paket där det verkar vettigt! Särskilt projektet är det lämpligt att dela upp i flera paket för att organisera och dela upp koden. I ett plattformsspel kanske man vill ha ett särskilt paket för spelfigurer, ifall den koden består av en större uppsättning klasser som hör nära ihop. I ett FTP-program kanske man vill ha ett paket för nätverkskod och ett för GUI-funktionalitet.

  • Använd namngivningsstandarden för paket. För att underlätta att skapa globalt unika paketnamn finns en standard där man inleder paketnamnet med sitt domännamn baklänges och därefter fortsätter med en identifierare inom domänen. För er del kan ett paketnamn till exempel vara "se.liu.ida.fffee123.tddd78.project.gameboard": Domän, studentkontonamn, kurs, uppgift (projektet), och ett delpaket inom projektet.

Att gömma information

Göm information

  • Instansvariabler (fält) ska normalt vara private, bland annat för att det möjliggör senare förändringar av datalagringen i en viss klass utan hänsyn till andra klasser som använder dessa data direkt.

    Som så många andra regler har denna regel undantag, främst vad gäller enklare datalagringsklasser som t.ex. Rectangle i Java. Vid varje instansvariabel som inte är private måste det dock dokumenteras noga varför detta är ett bättre alternativ.

  • När det gäller metoder måste ni tänka efter vilka som bör vara private, protected respektive public. Metoder som bara borde användas "internt" inom en klass ska till exempel vara privata.

Ingen "onödig" kod

Ta bort onödig kod

  • Undvik upprepad kod. Vi ser ofta kod som upprepas flera gånger, i samma klass eller i olika klasser, identiskt eller med vissa parametrar ändrade. Detta ska undvikas, eftersom det ger mer kod för både er och oss att förstå, mer kod att underhålla i framtiden, mer jobb när förändringar ska ske (och måste göras på många olika platser i koden), och så vidare. Till exempel:

    • Om en viss metod gör (mer eller mindre) samma sak flera gånger, ska den koden troligen brytas ut till en ny metod så att man istället kan anropa den metoden flera gånger. Om vissa värden skiljer sig (en gång loopar man över lista A, en gång över lista B) kan man ofta skicka med detta värde som parameter till metoden.

    • Om två relaterade klasser (i samma arvshierarki) har samma eller liknande metoder, kan detta kanske ersättas av en gemensam metod i en gemensam superklass.

    IDEA kan hjälpa till att hitta vissa former av duplicerad kod: Analyze | Locate Duplicates. Detta ersätter inte en egen kontroll av labben eller projektet!

    IDEA kan också hjälpa till att dela upp i delmetoder (markera kod, Refactor | Extract | Method).

  • Död kod ska tas bort, dvs. klasser, metoder eller fält som aldrig används. Undantaget är om det finns kod som inte är färdig, men där ni ändå vill visa att ni har kommit en bit på vägen. Sådan kod måste vara väl avgränsad och det måste framgå tydligt vilka delar (hela klasser eller metoder) som inte används och varför.

Kodinspektioner i IDEA

IntelliJ IDEA har stöd för över 600 "kodinspektioner" där utvecklingsmiljön automatiskt varnar för potentiella eller verkliga problem i programkoden.

I tidigare omgångar av kursen har vi sett att väldigt många av de problem och felaktigheter som leder till kompletteringar faktiskt kan fångas upp av inspektionerna! Samtidigt har vi begränsad tid att bedöma projekt, och vill använda den tiden på bästa sätt för att ge er konstruktiva icke-triviala kommentarer. Därför har vi från och med denna omgång ett krav på att alla labbar och projekt ska analyseras i IDEA innan inlämning: Kör Analyze|Inspect Code. Det ger er som studenter snabb feedback på vissa typer av problem, och det ger handledarna mer tid att fokusera på de sådant som är svårare att upptäcka mekaniskt, som t.ex. övergripande kodstruktur. Om ni absolut vill kan ni köra en annan miljö för utvecklingen men koden ska ändå analyseras i IDEA före inlämning.

Automatisk kodanalys kan så klart leda till falska varningar. Om man helt och hållet skulle undvika det skulle man också missa många väldigt värdefulla varningar. Därför kräver vi inte att alla projekt ska vara 100% fria från varningar. Däremot kräver vi att ni för varje varning:

  • Antingen fixar varningen så den försvinner,
  • eller kommenterar varningen i koden, nära själva varningen och motiverar varför ni faktiskt har gjort rätt och varför man inte bör ändra koden just där.

Ibland får man också varningar som helt enkelt beror på att man inte har hunnit skriva klart. Till exempel kan man få "unused declaration" när man har skapat en metod som ännu inte anropas någonstans. Dessa varningar får man så klart ignorera tillfälligt, så försvinner de när koden är färdig.

En JAR-fil med IDEA-inställningar finns tillgänglig för den som arbetar hemma. Ladda ner den och kör File | Import Settings. Ni kan sedan behöva göra vissa manuella val av profiler i File | Settings, t.ex. välja inspektionsprofilen TDDD78-2015-v1 i File | Settings | Inspections. Det kan hända att "färgkodningen" för inspektionsvarningar inte blir identisk med den på universitetets datorer.

Se även förklaringar av varningar.


Sidansvarig: Jonas Kvarnström
Senast uppdaterad: 2015-02-05