Er zijn goede uitgebreidere coding standards te vinden op het web, en als je serieus Java programma's gaat schrijven, is het zeker de moeite waard deze te bestuderen. Hieronder staan de belangrijkste tips voor het soort werk dat we bij P3 doen. Veel hiervan is, mutatis mutandi, ook relevant voor andere programmeertalen.
Vermijd idioot lange regels die niet goed te lezen zijn op een scherm of op printout's. Geef in een klasse eerst de static velden, dan de instance velden, dan de constructoren, en dan de methoden.
Klasse en
LangeNaamVoorKlasse
...Exception
methode(...) en
methodeMetLangeNaam(...)
variabelevariableMetLangeNaam óf
variabele_met_lange_naam.
Kies één van deze twee conventies en hou je daaraan.
final of final static velden
CONSTANTE en
CONSTANTE_MET_LANGE_NAAM.
field van type X teruggeven
get..., bijv.
getField() of getX()
field van type X veranderen
set..., bijv.
setField(...) of setX(...)
Waarom? De standaard OO- (encapsulatie-, modulariteits-,..) redenen: iedereen kan aan deze velden komen, zonder enige controle, en je kunt je representatie later niet meer veranderen.
Waarom? Static velden zijn als globale variabelen in niet-OO imperatieve talen.
final static Color BLACK = new Color(0,0,); final static int BREEDTE = 23;
Waarom? Maakt code beter leesbaar en beter onderhoudbaar.
* bij import.
Geef expliciet aan welke klassen je importeert,
en ga na dat alle geïmporteerde klassen ook daadwerkelijk gebruikt worden.
Waarom?
Maakt de code makkelijker te begrijpen voor lezers,
nb. óók voor jezelf als je na een paar uur weer naar je eigen code kijkt!
Bijvoorbeeld
import java.awt.Canvas;
import java.awt.Frame;
zegt meer dan
import java.awt.*;
/** .... Deze klasse is een extensie van de klasse Frame ...
**/
public class MijnFrame extends Frame{
...
/** ... Deze methode geeft een String terug ...
**/
public String getLabel(){ ...
/** The main method of the program. This is method is called
when the program starts.
**/
public void main(){
....
// gooi een exception als x null is.
if (x==null) throw new MijnException();
}
Geef wel kort commentaar over dingen die niet direct
duidelijk zijn uit de code.
Waarom? Overbodige get- en set-methoden maken de `interface' van een object onnodig ingewikkeld voor gebruiker, kunnen soms gewenste relaties tussen velden (invarianten) verstoren, en zijn vaak moeilijk te documenteren zonder naar interne representatiedetails te verwijzen. Tenslotte is het alleen maar extra werk.
equals ipv == om objecten te vergelijken, behalve als je test of een referentie null is.
Waarom?
Als een klasse de methode equals implementeert (of eigenlijk,
overschrijft),
dan is dat met de bedoeling dat deze gebruikt wordt, omdat
dit kennelijk de goede notie van gelijkheid is.
En als een klasse de methode equals niet implementeert,
krijg je gewoon de default implementatie van equals,
dwz. ==.
equals() niet wil overschrijven.
Waarom? De default-implementatie van equals()
is ==, dwz pointer-gelijkheid, en dat is meestal niet wat je wil.
clone() niet wil overschrijven.
Als clone() nuttig zou zijn voor de klasse,
implementeer (of eigenlijk, overschrijf) deze methode dan
(en voeg implements Cloneable toe).
Waarom? De default-implementatie van clone()
geeft een shallow copy en dat is meestal niet wat je wil.
if-statement vd vorm
if (x instanceof C) cx = (C)x;
else "evasive action"
main methode voor je applicatie in een eigen
klasse apart van alle andere klassen.
main.
Waarom?
Alle code die in main staat is niet her te gebruiken.
Te lange main methodes zijn symptomatisch van Java code
die niet object-geörienteerd is.
Waarom? Met een interface kun je meer kanten op; ihb, multiple inheritance wordt mogelijk. Bovendien is het duidelijker voor de gebruiker.
main
te definiëren die de klasse uitprobeert.
Bijvoorbeeld
public class TreeNode{
...
public TreeNode(String string){ ... }
public String getLabel() { ... }
public String[] getParams(){ ... }
public static void main (String[] args){
System.out.println("Testing de klasse TreeNode");
TreeNode tn = new TreeNode("bla(arg1, arg2)");
System.out.println(t.getLabel();
for (int i=0; i<tn.getParams().length;i++)
System.println(tn.getParams()[i]};
}
}
Tijdens het ontwikkelen van code schrijf je vaak even
hulpprogrammaatjes om iets te testen, die je later dan weer weggooit
of uitcommentarieerd; op deze manier blijven zulke testprogramma's bestaan
om later weer te gebruiken.
Natuurlijk is het niet altijd mogelijk of zinnig dit voor alle klassen te
doen.
Behalve als test, is dit handig als voorbeeld van hoe je de klasse gebruikt.
(In het voorbeeld hierboven zouden we natuurlijk ook het geval van een parameterloze TreeNode even moeten uitproberen.)
Er zijn manieren om het testen serieuzer en georganiseerder aan te pakken, bijv door het schrijven van unit tests mbv. junit.
/**
* De interface van een probleem dat de General Problem Solver (GPS)
* op kan proberen te lossen.
* Een object van type ProbleemToestand is een toestand in de
* toestandsruimte waarin de GPS zoekt.
*
* @author Piet Venema 02123456
* @see java.awt.Frame
**/
public interface ProbleemToestand implements Comparable {
...
De eventuele tags, zoals @author, komen na de beschrijving.
Zet de namen van de makers bovenaan alle files met @author in de javadoc
documentatie, dat is voor ons handig bij het nakijken.
/**
* Voeg een element toe in het begin van de rij.
*
* @param element het toe te voegen element.
* @throws StackFullException als er geen ruimte meer beschikbar is
**/
public void addFirst(Object element);
NB. de eerste zin van de beschrijving moet dus een op zichzelf staande,
beknopte, beschrijving zijn van wat de methode doet. Javadoc gebruikt
deze zin namelijk los van de complete beschrijving op sommige plaatsen.
De eventuele tags, zoals @param, @result,
en @throws, komen na de beschrijving.
Bij hele simpele gevallen
mag je de @param (cq. @result) wel weglaten.
mits je goedgekozen namen voor de parameters (cq. methode) kiest.
/**
* Aantal subtrees van deze Labtree
**/
private int count;
tenzij er absoluut geen misverstand kan zijn over wat het veld voorstelt.
Documenteer ook eventuele verbanden tussen velden (zogenaamde invarianten), bijvoorbeeld:
private boolean gevonden; private Vector kortste_pad; // (gevonden == false) desda (kortste_pad == null )
Vector gebruikt, als veld of als parameter,
waarvan je weet of waarvan het de bedoeling is dat de elementen van
een bepaalde klasse zijn (bijv. het is een Vecter van Labtree's)
documenteer dit.
Dit geldt ook als je andere container-types gebruikt.
(In Java 1.5 zal het typesysteem van Java uitgebreid worden met zogenaamde generics, zodat je het over dit soort eigenschappen in het typesysteem kunt uitdrukken).
Een uitgebreidere documententie over javadoc is
How to Write Doc Comments for the Javadoc Tool
Voor voorbeelden van javadoc documentatie, bekijk
de documentatie van java API classen.
javadoc *.javaen bekijk de geproduceerde HTML pagina's goed met een browser, dwz. léés deze pagina's echt en kijk er kritisch naar. Vraag jezelf af:
Wees erop bedacht dat javadoc soms stukken commentaar negeert als je je niet aan de javadoc syntax regels houdt.