Java's kodkonventioner och arbete i grupp Anders Hagsten Lunds tekniska högskola [email protected] 2005-02-13 Sammanfattning Denna djupstudie handlar om faktorerna som ger tydlig och lättläst kod och hur namngivningen påverkar detta. Med utgångspunkt från Java's kodkonventioner behandlas vanligt förekommande namngivningsproblem som till exempel för långa eller intetsägande namn. Konventionernas funktion i att lösa problem när man arbetar i grupp studeras även närmare. Innehåll 1 Inledning 3 2 Konventioner 3 2.1 Syftet med konventioner . . . . . . . . . . . . . . . . . . . . . . . 3 2.2 Kort om Javas kodkonventioner . . . . . . . . . . . . . . . . . . . 3 3 Ofta förekommande namngivningsproblem 4 3.1 Magiska nummer . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2 Långa namn . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 5 3.3 Korta namn . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 3.4 Förkortningar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 3.5 Intetsägande namn . . . . . . . . . . . . . . . . . . . . . . . . . . 6 3.6 Vilseledande klassnamn 3.7 Felstavade namn . . . . . . . . . . . . . . . . . . . . . . . 6 . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 4 Utvecklingsplattformen Eclipse 7 5 Projekterfarenheter 7 5.1 Allmänt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 5.2 De vanligaste problemen . . . . . . . . . . . . . . . . . . . . . . . 8 6 Slutsatser 8 7 Vidare undersökningar 8 2 1 Inledning Namngivning är något som alltför ofta ställer till förtret. Det många glömmer bort är att när man namnger något medför detta också förväntningar på funktionen hos detta. När man arbetar själv fungerar det bra att döpa saker efter eget tyckte men när man arbetar i grupp måste mer eftertanke nnas. Detta på grund av att alla i gruppen inte pratar samma språk. Precis som i matematiken kan man i kod skriva en sak på era olika sätt men med samma resultat. Här kommer kodkonventioner in i bilden för att avgöra vilket sätt som är bäst. I denna rapport tar jag upp syftet med konventioner och vanliga problem som uppstår när man arbetar i grupp. Jag har valt att utgå från Java's kodkonventioner då detta är mitt primära språk. 2 Konventioner 2.1 Syftet med konventioner Fördelarna med att ha konventioner för namngivning, formatering och hur man skriver den faktiska koden är många. Det som kan vara lätt att glömma, när man programmerar, är att någon i framtiden faktiskt ska underhålla koden genom att till exempel lägga till ny funktionalitet. Har den som skrivit koden då valt att följa givna konventioner underlättas underhållsarbetet. Det är helt enkelt lättare att sätta sig in i koden då den följer en vedertagen standard. För företag är detta extremt viktigt då 80% av livstidskostnaden för mjukvaran utgörs av underhållsarbete. Vidare är det så att anställda tenderar att byta jobb ibland eller företaget omorganiseras. Det gör att personen som skrev koden från början inte alltid nns tillgänglig för att förklara den. Faktum är att det mycket sällan är personen som skrev programmet som underhåller det. 2.2 Kort om Javas kodkonventioner Företaget bakom Java, Sun Microsystems, tillhandahåller på sin hemsida Javas kodkonventioner [1]. Detta dokument specicerar allt från metodernas längd till hur man bör namnge sina variabler. Nedan presenteras de viktigare poängerna i dokumentet: • Formatering: Undvik rader längre än 80 tecken. Detta för att många program har svårt att hantera längre rader. Ett exempel på detta är pro- AT Xsom denna text formaterats av. Vidare anges att metoder grammet L E ej bör vara över 150 rader och hela Java-ler ej bör överstiga 2000 rader. • Klassnamn: Klassnamn ska alltid vara ett, eller en kombination, av sub- stantiv. Samtliga substantiv i klassens namn ska ha stor bokstav. Exempel: ResultGenerator.java • Gränssnitt: Gränssnitt ska, precis som klassnamn, vara en kombination av substantiv samt börja med stor bokstav. Javas kodkonventioner ger inga riktlinjer om hur de bör namnges men om man tittar i Javas API ser man att de ofta namges med adjektiv eller substantiv. Adjektiv har fördelen att de ger en indikation på beteendet hos gränssnittet. Exempel: Observer.java, Adjustable.java 3 • Metodnamn: Metodnamn ska innehålla verb för att tala om vad man gör och gärna kombineras med substantiv eller adjektiv för att tydliggöra metodens eekt på objektet. Första bokstaven i metodnamnet ska alltid vara gemen och efterföljande inre ord ska börja med en versal. Exempel: printCompetitionData(), isEmpty(), getDriver() • Variabler: Variabelnamn skrivs på samma sätt som metodnamn. Pro- grammerare som skolats i andra programspråk bör vara medvetna om att variabelnamn som börjar med _ och $ ej, enligt konvention, bör användas men ändå tillåts. Variabelnamn bör vara korta men ändå meningsfulla och indikera användningsområdet. Korta namn som exempelvis i bör undvikas utom när de används tillfälligt inuti en metod som räknare eller dylikt. Exempel: int startNumber, ArrayList startTimes • Konstanter: Konstanter skrivs med versaler och om konstantens namn består av era ord separeras dessa med _. Exempel: 3 static final int MAX_SIZE = 4 Ofta förekommande namngivningsproblem När man studerar kodexempel nns det fall som är ofta förekommande. Dessa fall kommer vi titta närmare på här. 3.1 Magiska nummer Magiska nummer [3] är tal som bara dyker upp från ingenstans och används i koden. Siror som 1, -1, 0 och 2 anses inte vara magiska då användandet av dessa är vida spritt. Exempel: class Physics { } /** * This method calculates the length contraction of an object. * @param v - the velocity of the object. * @param l - the length of the object. * @return The length as experienced by the observer. */ public double lengthContraction(double v, double l) { double ratio = (v * v) / (299792458 * 299792458); double obsL = l * Math.sqrt(1- ratio); return obsL; } Exemplet ovan är väldigt enkelt men visar ändå på hur otydligt det blir med magiska nummer. Det fungerar felfritt men det blir svårt att läsa. När programmet sedan ska underhållas kommer det med största sannolikhet ställa till problem för de som ska göra det. Tilldela istället en konstant detta värde och skriv på följande sätt: 4 class Physics { /* Speed of light in vacuum (m/s) */ public static final int SPEED_OF_LIGHT = 299792458; } /** * This method calculates the length contraction of an object. * @param v - the velocity of the object. * @param l - the length of the object. * @return The length as experienced by the observer. */ public double lengthContraction(double v, double l) { double ratio = (v * v) / (SPEED_OF_LIGHT * SPEED_OF_LIGHT); double obsL = l * Math.sqrt(1 - ratio); return obsL; } Detta är mycket tydligare, mer lättanvänt och om man, mot förmodan, skulle vilja ändra ljusets hastighet i vakuum behöver man bara göra det på ett ställe. 3.2 Långa namn Långa namn är en eekt av att man vill vara övertydlig i metoden, klassens eller variabelns funktion. Fördelarna med detta är att man eventuellt inte behöver läsa dokumentationen utan det inses direkt vad den gör. Nackdelarna är dock era, exempelvis får eventuellt inte anropen plats på .java-lens rader om man ska följa kodkonventionerna på max 80 tecken på en rad. Vidare blir de arbetsamma att skriva om man skriver i en lättare editor utan komplettering och därmed ökas risken för slarvfel. Ytterligare ett problem är att långa beskrivande namn begränsar möjligheterna att lägga till ny funktionalitet utan att döpa om klassen och därför medför extra arbete. Ett exempel på långt namn är ResultGeneratorSortedResultWithPlaceNumbers som jag lånat från projektet jag just nu coachar. Detta är ett exempel på övertydlig namngivning som resulterar i långa namn. Mycket av det som inkluderas i namnet borde stå i dokumentationen för klassen. Detta problem påtalades och klassens namn ändrades till ResultGeneratorByRank som ger samma intryck men på ett tydligare sätt. 3.3 Korta namn Korta namn såsom i, j och c bör, enligt Java's kodkonventioner, endast användas som temporära variabler inne i metoder. I t.ex forloopar är det närmast konvention att man döper första loop-variabeln till i och sen om det nns en inre loop döper den till j och så vidare. Problemet är att programmerare har lätt att döpa sina variabler till temp eller liknande när de testar fram nya lösningar för att sedan glömma ändra dem till förklarande namn. Exempel: public String giggleLoop(int nbrGiggles) { String temp = ""; 5 for (int i = 0; i<nbrGiggles; i++) { temp += "giggle"; } return temp; } Ovan är ett exempel på hur det ofta ser ut. Programmeraren har förstått att han behöver en sträng att bearbeta men inte brytt sig om att ändra det när metoden sedan färdigställts. Nedan visas resultatet efter namnbyte. public String giggleLoop(int nbrGiggles) { String giggleSequence = ""; for (int i = 0; i<nbrGiggles; i++) { giggleSequence += "giggle "; } return giggleSequence; } Trots att detta exempel är väldigt trivialt ser man redan här att det blir mer lättläst. Detta kan härledas till att namnet temp används ofta och i olika sammanhang vilket gör det svårt att förstå betydelsen i varje fall utan att verkligen kontrollera. 3.4 Förkortningar Förkortningar bör inte användas om inte förkortningen är mer använd än or- getURL() med getUniversalResourceLocator(). I vissa, dock ofta förekommande, fall är det det och det därmed blir tydligare med dem. Jämför till exempel mer frågan om att nna ett gemensamt språk än att använda förkortningar eller ej. I vårt projekt fanns en variabel som höll reda på startnumret för en förare. Denna variabel hade till en början inte mindre än tre namn: nbr, startnum- 1 get- och set- ber och startNbr. Dessa hade den erhållit från handskrivna metoder. Detta upptäcktes emellertid snart och ändrades till startNumber. Det hela berodde på att programmerare med olika stil skrivit metoderna för att hämta och sätta variabeln. 3.5 Intetsägande namn I vårt projekt skrev vi en datastruktur för de tävlande för att sedan kunna bearbeta dem på olika sätt, t.ex sortera och visa alla som inte kom i mål. Problemet var att denna namngavs till DataStructure. Detta namn talar bara om vad vi redan vet, nämligen att det är en datastruktur. Det talar inte om på något sätt hur klassen kan användas och gav ingen indikation på vilken sorts information man ck ut. Klassens namn byttes senare ut mot DriverList som säger mycket mer om form och funktion. 3.6 Vilseledande klassnamn Det första som möter programmeraren när han ska använda en klass är klassnamnet. Klassnamnet ger ett initialt tips till programmeraren om hur den kan 1 Eclipse kan automatgenerera så kallade getters och setters utifrån ett variabelnamn. 6 användas. Det är därför viktigt att namngivandet inte är vilseledande, t.ex om vi hade döpt DriverList ovan till DriverTable ger det ett helt annat intryck om klassens funktion för programmeraren. 3.7 Felstavade namn När man programmerar skriver man oftast på engelska och då detta inte är vårt modersmål kan ibland mindre problem uppstå på grund av detta. De esta är dock medvetna om sina begränsningar när det gäller stavning på det engelska språket och väljer därför att hellre hitta en synonym än att riskera att stava fel. Något konkret exempel från vårt projekt lyckades jag inte nna så jag har konstruerat ett: competitionType och raceType. Detta är typiskt en omskrivning som sker om man är osäker på stavning. De betyder, i princip, samma sak men kräver olika ansträngning att stava. Det största problemet med felstavade namn är att kompileringsfelen blir väldigt svåra att hitta i koden. Vi tenderar att inte upptäcka stavfel om vi inte letar efter dem [4]. 4 Utvecklingsplattformen Eclipse De projekt som vi genomför använder sig av Eclipse som utvecklingsverktyg. Eclipse är bra då layoutdelarna från Java's kodkonventioner i stort sett är implementerade i textformateringen som det använder sig av. Vid skapande av klasser får man en varning av programmet om man använder sig av liten bokstav och det formaterar koden med hjälp av några enkla knapptryckningar. Några missar nns ändå där, exempelvis när man namnger metoder får man ingen varning om dessa skulle ha stor bokstav och detsamma gäller för variabelnamn. Stödet för namnbyte är exemplariskt, med refektoriseringsverktyget kan man byta till exempel ett metodnamn och samtidigt döpa om alla anrop till denna, något som kan ta lång tid i en vanlig text-editor så som Emacs. 5 5.1 Projekterfarenheter Allmänt I början av ett projekt är det bra att fastställa vilka konventioner som skall råda. I vårt projekt bestämde vi under första mötet att Java's kodkonventioner skulle gälla samt att alla skulle fräscha upp minnet genom att läsa dessa. Detta gav oss en slags lagstiftning inom projektet. Under projektets gång kunde vi sedan lösa många dispyter med hjälp av dessa, till exempel hur man skriver if-satser eller vilket som är det bästa namnet på en klass. Att ha en lag att hänvisa till gjorde vår styrande roll enklare. En annan eekt som märktes av var att deltagarna, allt eftersom projektet fortlöpte, blev mycket mer medvetna om namngivningens betydelse. Detta beror med stor säkerhet på att de aldrig tidigare behövt arbeta med ett program under så lång tid och under ständiga förändringar i kravspecikationen. 7 5.2 De vanligaste problemen Under vårt projekt uppstod merparten av de problem som nns beskrivna i kapitel 3 men vissa var mer frekventa, exempelvis korta och långa namn. Problemet med korta namn uppstod i början av projektet då arbetssättet och stressfaktorn var helt ny för deltagarna. Detta var första gången de skrev kod som inte bara skulle fungera utan även vara läsbar. Jag tog upp problemet med de korta namnen under ett planeringsmöte och ck bra respons från deltagarna, de hade också märkt att det ställde till problem. Eekten blev att det gick från en extrem till en annan och vi ck väldigt långa namn som talade om exakt var och hur det namngivna användes. De långa namnen var bra att ha under utvecklingsfasen men när projektet började röra sig mot sitt slut fann vi att deras roll var utspelad och namnen refaktoriserades till kortare versioner. 6 Slutsatser Namngivning utan konvention fungerar så länge man arbetar i små grupper på mjukvara som inte skall underhållas men så fort utvecklingen sker i större grupp blir namngivningen kritisk. Dåliga namn för med sig kostsamma misstag som kunde sparat både tid och pengar om några extra minuter lagts på namngivning. Det har visat sig smidigt att tidigt i ett projekt välja en konvention som skall gälla då det ger en lagstiftning åt projektet. Dispyter kan lösas snabbare när man har en lag att hänvisa till istället för att dras in i ändlösa diskussioner. Slutligen bör sägas att Java's kodkonventioner inte har lösningen på alla namngivningsproblem utan det måste nnas en dialog inom projektgruppen. 7 Vidare undersökningar För den som vill utforska namngivning och dess inverkan vid projektarbete ges här ett par förslag för vidare undersökningar: • Hur man får alla deltagare att följa konventionen När man arbetar i större grupper är det ofelbart så att någon har en egen konvention som denne anser är bättre. Hur styr man gruppen så att alla följer samma konvention? • Genomför ett projekt utan kodkonventioner Undersök vilka konsekvenser ett projekt utan kodkonvention ger. Detta kan vara svårt att göra under rådande kursformer då den endast är åtta veckor lång men ändå väldigt intressant. • Vilka roller har verktygen i hur väl kodkonventioner åtlyds Eclipse, som används vid våra projekt, innehåller verktyg som xar till de mindre delarna av Java's kodkonventioner, exempelvis 80 tecken på en rad och stor bokstav på klassnamnet. Vad händer om exempelvis Emacs istället används och hur väl kommer konventionerna att åtföljas? • Skriv antipatterns för namngivning Roedy Green tar i sin text How to Write Unmaintainable Code upp en rad av vad man kan kalla antipatterns och då bland annat vad gäller 8 namngivning. Green är dock inte så tydlig i sitt framställande av dessa. Använd Greens text för att nna tydligare antipatterns som kan vara till nytta för andra som går kursen. 9 Referenser [1] Scott Hommel. Java Code Conventions http://java.sun.com/docs/codeconv/CodeConventions.pdf [2] Cunningham med era. System of Names http://www.c2.com/cgi/wiki?SystemOfNames [3] Edward Parrish. Document and Organize Java Code http://www.edparrish.com/cs12/04f/codedoc.php#ovw [4] Roedy Green How to Write Unmaintainable Code http://mindprod.com/unmain.html 10