24. Kódolási konvenciók

A kódolási konvenciók a következők miatt szükségesek:

  • A szoftver életciklus költségének 80%-a mehet el a karbantartásra
  • A szoftvereknek csak kis résztét tartja karban az eredeti fejlesztő
  • A kódolási konvenciók javítják az olvashatóságot, segítve a későbbi megértést

A konvenciókhoz mindenkinek alkalmazkodni kell a programozás folyamán. Mindenkinek.

24.1. Fájlok szervezése

Minden java forrásállomány egyetlen publikus osztályt vagy interfészt tartalmaz, de ezen túl tartalmazhat még privát, a forrásállományon kívül kapcsolattal nem rendelkező további osztályokat és interfészeket. A publikus típust javasolt először leírni. A publikus típus neve és a forrásfájl neve meg kell, hogy egyezzen, beleértve a kisbetű-nagybetű különbségét is.

A Java forrásállomány a következő elemeket tartalmazza:

  • Kezdő megjegyzés
  • Csomag és import kifejezések
  • Osztály és interfész deklarációk

Kezdő megjegyzés

Minden forrásállománynak kellene tartalmaznia egy C stílusú megjegyzést a (publikus) osztály nevével, verzió információival, dátummal és szerzői jogokkal:

/*
* Osztálynév
*
* Verzió információk
*
* Dátum
*
* Copyright
*/

Javasolt a hagyományos megjegyzés helyett/mellett dokumentációs megjegyzéseket is alkalmazni.

Csomag és import kifejezések

A megjegyzés utáni első nem üres sor a csomag kifejezés kell, hogy legyen. Azután következhetnek az import kifejezések:

package java.awt;
import java.awt.peer.CanvasPeer;

Megjegyzés: Az egyedi csomagnév első része csupa angol kisbetűből álló, fordított doménnév.

Osztály és interfész deklaráció

A következő táblázat összefoglalja az osztály és interfész deklaráció szabályait:

osztály vagy interfész dokumentációs megjegyzés (/**...*/)
osztály vagy interfész kifejezés
osztály vagy interfész megvalósítási megjegyzés (/*...*/), ha szükséges Ez a megjegyzés olyan információkat tartalmaz, amik csak a megvalósítás részleteiről szólnak, az osztály későbbi felhasználása során nem lesz szükség ezekre az információkra. Ilyen módon a dokumentációs megjegyzésbe nem kerül bele.
osztály (statikus) változók Először a publikus, majd védett, csomag szintű, és végül privát változók szerepeljenek.
példányváltozók Ugyanolyan láthatósági sorrendben, mint az osztályváltozók
konstruktorok
metódusok A metódusok feladat szerint és láthatóság szerint csoportosítva.

24.2. Behúzás

Megjegyzés: Ebben a jegyzetben a nyomtatás speciális tördelése miatt néhol eltértünk a következő szabályoktól.

2 vagy 4 szóköz, vagy a tabulátor karakter használata célszerű.

A sorok hossza ne legyen több 80 karakternél. Bizonyos esetekben érdemes 70 karakterben maximálni a sor hosszát.

Hosszú sorok tördelése

Ha egy kifejezés nem fér el egy sorban, a következők alapján tördeljünk:

  • Törjünk vessző (,) után
  • Törjünk egy operátor előtt
  • Részesítsük előnyben a magasabb szintű művelet előtt való törést
  • Igazítsuk az új sor kezdetét az előző sor ugyanolyan szintű kifejezéséhez
  • Ha az előzőek alapján zavaros lesz az eredmény, vagy így is túl hosszú a sor, akkor a szokásos behúzás kétszeresét (8 karakter) is alkalmazhatjuk

Nézzünk egy példát illusztrációként:

someMethod(longExpression1, longExpression2, longExpression3,
        longExpression4, longExpression5);
var = someMethod1(longExpression1,
                someMethod2(longExpression2,
                        longExpression3));

A következő két példa aritmetikai kifejezések töréséről szól. Az első példa elfogadható, a törés alkalmazkodik a logikai szerkezethez, de a második nem javasolt.

longName1 = longName2 * (longName3 + longName4 - longName5)
           + 4 * longname6;
longName1 = longName2 * (longName3 + longName4
                       - longName5) + 4 * longname6;

A következő két kód metódusdeklaráció bemutatására szolgál. Az első a szokásos esetet mutatja, de a másodiknál ez nem működne. Ezért ott a fix kétszeres behúzást alkalmazzuk:

someMethod(int anArg, Object anotherArg, String yetAnotherArg,
           Object andStillAnother) {
    ...
}
private static synchronized horkingLongMethodName(int anArg,
        Object anotherArg, String yetAnotherArg,
        Object andStillAnother) {
    ...
}

Az if utasítás törése esetén is a dupla behúzás javasolható, az egyszeres behúzás nehezebben felismerhetővé teszi a törzset. Az első tehát nem célszerű, helyette a második vagy harmadik javasolható:

if ((condition1 && condition2)
    || (condition3 && condition4)
    ||!(condition5 && condition6)) {
    doSomethingAboutIt();
}
if ((condition1 && condition2)
        || (condition3 && condition4)
        ||!(condition5 && condition6)) {
    doSomethingAboutIt();
}
if ((condition1 && condition2) || (condition3 && condition4)
        ||!(condition5 && condition6)) {
    doSomethingAboutIt();
}

Végül három jó példa a feltételes operátor alkalmazására:

alpha = (aLongBooleanExpression) ? beta : gamma;
alpha = (aLongBooleanExpression) ? beta
                                 : gamma;
alpha = (aLongBooleanExpression)
        ? beta
        : gamma;

24.3. Megjegyzések

A Java programok kétféle megjegyzést alkalmaznak: dokumentációs és implementációs megjegyzést. Az utóbbi a C++ nyelvhez hasonló, míg az előbbi a Java specialitása. (Megjegyzés: Ezt a bevált ötletet a PHP is átvette.) A dokumentációs megjegyzés /** és */ közötti karaktersorozat. A javadoc segédprogram képes ez alapján automatikusan generálni HTML alapú, teljesen hipertext felépítésű dokumentációt.

A dokumentációs megjegyzés a forráskódot dokumentálja olyan megközelítésben, hogy az alapján a későbbi megértés, az osztály felhasználása maradéktalanul megoldható legyen a kód kézzel történő végignézése nélkül is.

24.3.1 Implementációs megjegyzések

A programok négyféle implementációs megjegyzést tartalmazhatnak: blokk, egysoros, nyomkövetési és sorvégi.

Blokk megjegyzések

A blokk megjegyzések leírást tartalmaznak a fájlhoz, metódushoz, adatszerkezethez vagy algoritmushoz. Fájl esetén a fájl elején, a többi esetben a metódus belsejében szokás elhelyezni, alkalmazkodva a megjegyzéssel ellátott egység behúzásához.

A blokk megjegyzés egy üres sorral kezdődik és fejeződik is be:

/*
* Blokk megjegyzés.
*/

Egysoros megjegyzések

A rövid megjegyzéseket egyetlen sorba írva, a bekezdési szintet figyelembe véve lehet elhelyezni. Ha nem lehet egy sorba írni, akkor blokk megjegyzést érdemes alkalmazni. Egy üres sor után érdemes írni a megjegyzést, hogy vizuálisan láthatók legyenek az összefüggések.

if (feltétel) {

    /* Eset leírása. */
    ...
}

Nyomkövetési megjegyzések

Nagyon rövid megjegyzéseket az adott kódsor végére is lehet írni, megfelelő közzel elválasztva a kódtól. Célszerű az egymás utáni nyomkövetési megjegyzéseket azonos pozícióban kezdeni. A következő példa mutatja a használat módját:

if (a % 2 == 0) {
    return TRUE;            /* páros szám esetén kész */
} else {
    return isPrime(a);      /* különben vizsgáljuk */
}

Természetesen alkalmazható a // is, de csak következetesen, nem keverve a két módot. Nem érdemes olyan megjegyzéseket alkalmazni, amik a későbbi megértést nem segítik elő jelentősen.

Megjegyzés: A jegyzet példái oktatási célból készültek, sokszor túl részletesen kommentezve. Valódi fejlesztéskor csak a ténylegesen szükséges megjegyzéseket érdemes elhelyezni.

24.3.2 Dokumentációs megjegyzések

A következő példa egy jó áttekintést ad az alapokról:

/*
* @(#)Blah.java        1.82 99/03/18
*
* Copyright (c) 1994-1999 Sun Microsystems, Inc.
* 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
* All rights reserved.
*
* This software is the confidential and proprietary
* information of Sun Microsystems, Inc. ("Confidential
* Information").  You shall not disclose such Confidential
* Information and shall use it only in
* accordance with the terms of the license agreement
* you entered into with Sun.
*/
package java.blah;
import java.blah.blahdy.BlahBlah;
/**
* Class description goes here.
*
* @version     1.82 18 Mar 1999
* @author     Firstname Lastname
*/
public class Blah extends SomeClass {
    /* A class implementation comment can go here. */
    /** classVar1 documentation comment */
    public static int classVar1;
    /**
     * classVar2 documentation comment that happens to be
     * more than one line long
     */
    private static Object classVar2;
    /** instanceVar1 documentation comment */
    public Object instanceVar1;
    /** instanceVar2 documentation comment */
    protected int instanceVar2;
    /** instanceVar3 documentation comment */
    private Object[] instanceVar3;
    /**
     * ...constructor Blah documentation comment...
     */
    public Blah() {
        // ...implementation goes here...
    }
    /**
     * ...method doSomething documentation comment...
     */
    public void doSomething() {
        // ...implementation goes here...
    }
    /**
     * ...method doSomethingElse documentation comment...
     * @param someParam description
     */
    public void doSomethingElse(Object someParam) {
        // ...implementation goes here...
    }
}

A legfelső szintű osztályok és interfészek a sor elején kezdődnek, míg a tagok behúzva. Az osztályok és interfészek dokumentációs megjegyzésének első sora nem behúzott, a további sorok egy szóközzel bentebb kezdődnek. Minden tag (osztály, interfész, adattag, metódus, konstruktor) dokumentációs megjegyzésének első sora 4 szóközzel, a továbbiak 5 szóközzel bentebb kezdődnek. A további szintek ugyanígy, értelemszerűen.

24.4. Deklarációk

A könnyű kommentezhetőség (és a jobb átláthatóság) érdekében minden sor csak egy deklarációt tartalmaz:

int level; // indentation level
int size;  // size of table

Kevésbé javasolható:

int level, size;

Semmiképpen ne kerüljenek egy sorba különböző típusú deklarációk:

int foo,  fooarray[]; //WRONG!

A kód még könnyebben olvasható lesz, ha a típusok, azonosítók és megjegyzések táblázatszerűen tabuláltak:

int        level;          // indentation level
int        size;           // size of table
Object     currentEntry;   // currently selected table entry

A lokális változókat lehetőleg már a deklarációnál inicializáljuk. Az egyetlen ésszerű kivétel, ha a kezdőérték bonyolult számításokkal fog előállni.

24.4.1 A deklaráció helye

A deklarációk a blokk legelején legyenek. Bár vannak érvek mellette, mégsem érdemes később, az első használat helyén deklarálni az azonosítókat. Ennek az lenne a hátránya, hogy akadályozná az érvényességi tartományon belüli hordozhatóságot.

void myMethod() {
    int int1 = 0;         // beginning of method block
    if (condition) {
        int int2 = 0;     // beginning of "if" block
        ...
    }
}

Az egyetlen logikus kivétel, amikor a for ciklusban hozzuk létre a ciklusváltozót:

for (int i = 0; i < maxLoops; i++) { ... }

Érdemes elkerülni, hogy egy lokális deklaráció elfedjen egy magasabb szintű deklarációt:

int count;
...
myMethod() {
    if (condition) {
        int count = 0;     // AVOID!
        ...
    }
    ...
}

24.4.2 Osztály és interfész deklaráció

Amikor osztályt vagy interfészt deklarálunk, érdemes megfogadni a következőket:

  • A metódus neve és a ’(’ között ne legyen elválasztó.
  • A blokk kezdő ’{’ az adott sor végén legyen
  • A blokkzáró '}' behúzása megegyezik a blokk kezdetének behúzásával. Ha üres a blokk, a két zárójelet ne válassza el köz.
    class Sample extends Object {
        int ivar1;
        int ivar2;
        Sample(int i, int j) {
            ivar1 = i;
            ivar2 = j;
        }
        int emptyMethod() {}
        ...
    }
  • A metódusokat válasszuk el egy üres sorral.

24.5. Utasítások

Egyszerű utasítások

Soronként csak egy utasítás szerepel:

argv++;       // Correct
argc--;       // Correct 
argv++; argc--;       // AVOID!

Összetett utasítások

Az összetett utasítás ’{’ és ’}’ között utasítások sorozatát tartalmazza. A tartalmazott utasítások egy közzel (4 szóközzel) bentebb kezdődnek, mint az összetett utasítás blokk.

Feltételes utasítások

Az if, if-else, if else-if else utasítások:

if (condition) {
    statements;
}

if (condition) {
    statements;
} else {
    statements;
}

if (condition) {
    statements;
} else if (condition) {
    statements;
} else{
    statements;
}

Mindig alkalmazzuk a blokkot jelző { és } karaktereket. E nélkül több a hibalehetőség.

if (condition) //AVOID! THIS OMITS THE BRACES {}!
    statement;

A switch szokásos formája:

switch (condition) {
case ABC:
    statements;
    /* falls through */

case DEF:
    statements;
    break;

case XYZ:
    statements;
    break;

default:
    statements;
    break;
}

Mindig írjuk ki a break utasítást. Ha nem kell kilépni a case végén, akkor megjegyzéskén szerepeljen.

Ciklusutasítások

A for utasítás formája a következő:

for (initialization; condition; update) {
    statements;
}

A törzs nélküli utasítás blokk nélkül, ’;’-el írható:

for (initialization; condition; update);

Amikor használjuk a ’,’ operátort az inicializáló vagy a növekmény részben, akkor a kód nehezebben áttekinthető lesz. Érdemes megfontolni, hogy ilyen esetben nem jobb-e az inicializációt a ciklus elé írni.

A szokásos és az üres törzsű while utasítás formája:

while (condition) {
    statements;
}

while (condition);

A do-while formája:

do {
    statements;
} while (condition);

try-catch utasítás

try {
    statements;
} catch (ExceptionClass e) {
    statements;
}
try {
    statements;
} catch (ExceptionClass e) {
    statements;
} finally {
    statements;
}

24.6. Elválasztók

Az üres sorok logikailag tagolják, és így olvashatóbbá teszik a kódot.

Két üres sort alkalmazzunk osztályok elválasztására. Egy sor elegendő a következő esetekben:

  • Metódusok között
  • Változó deklarációk és a blokk első utasítása között
  • Blokk vagy egysoros megjegyzés előtt
  • A metódus logikai egységei között (jellemzően 3-10 soronként)

Üres szóközöket alkalmazunk a következő esetekben:

  • A kulcsszó és a ’(’ között:
    while (true) {
          ...
      }
  • A paraméterlistában vesszők után
  • A ’.’ kivételével minden bináris operátor előtt és után, unáris operátorok esetén azonban nem alkalmazzuk:
      a += c + d;
      a = (a + b) / (c * d);

      while (d++ = s++) {
          n++;
      }
      printSize("size is " + foo + "\n");
  • A for utasításban a ‘;’-k után:
    for (expr1; expr2; expr3)
  • Explicit konverzió esetén:
    myMethod((byte) aNum, (Object) x);
      myMethod((int) (cp + 5), ((int) (i + 3)) + 1);

24.7. Elnevezési konvenciók

Az elnevezési konvenciók segítségével jobban olvasható programkódot kapunk. Már a név formája alapján lehet tudni, hogy konstans, csomagnév, osztály vagy változó.

Azonosító típusa Szabály Példák
Csomag Az egyedi csomagnév mindig csupa kisbetűkből áll. Az internet domén név legfelső szintű eleme (pl. com, hu) után a vállalat neve, majd szükség szerint további alcsomag-nevek következnek ponttal elválasztva. Csak az angol abc betűi használhatók. com.sun.eng
com.apple.quicktime.v2
edu.cmu.cs.bovik.cheese
Osztály Az osztálynév többnyire főnév, a kezdőbetű és a köztes szókezdő betűk nagybetűk, a többi kisbetű. Az aláhúzás karakter nem alkalmazható. Ne legyen rövidítés, mindig a teljes szavakat tartalmazza. class Raster;
class ImageSprite;
Interfész Lehet ékezetes karaktereket is alkalmazni, bár ezt bizonyos fejlesztőkörnyezetek (tehát nem a Java!) nem engedik. Emiatt általában érdemes kerülni az ékezetek használatát. interface RasterDelegate;
interface Storing;
Metódus A metódus neve többnyire ige, a köztes szavak kezdőbetűi kivételével minden kisbetűs, a legelső betű is. run();
runFast();
getBackground();
Változó A változónevek rövidek, de beszédesek legyenek. Az adott érvényességi tartomány bármelyik pontján egyértelmű legyen a szerepe. Lehetőleg kerüljük az egy karakteres neveket, kivéve a ciklusváltozók szokásos nevei (i, j, stb.).
A köztes szavak kezdőbetűi kivételével minden kisbetűs, a legelső betű is.
int i;
float myWidth;
Konstans A konstans változók neve csupa nagybetűs, a többszavas összetételeket az ’_’ karakterrel kell elválasztani. static final int MIN_WIDTH = 4;
static final int MAX_WIDTH = 999;
static final int GET_THE_CPU = 1;