Tiszta kód – 1. rész: nevek

Bevezető

A nevek mindenhol szerepelnek a kódunkban. Nevet adunk a változóknak, függvényeknek, osztályoknak, enumoknak, fájloknak, stb. Talán érdemes írni róla, hogy mitől lesznek a nevek jók. A következő pontokban ezt igyekszem összefoglalni. Ne feledjük: a neveket azért adjuk, hogy kommunikáljunk velük.

wtf-per-minute

A példákat a Tiszta kód sorozatból válogattam. A post elolvasása nem helyettesíti az élményt!

Érthető cél

Minden névnek az a célja, hogy kommunikálja írója szándékát.

int d; // elapsed time in days

int elapsedTimeInDays;
int daysSinceCreation;
int daysSinceModification;
int fileAgeInDays;

A fenti példában látható d név nem túl informatív. Ha a változót sok helyen használjuk, előfordulhat, hogy a kontextusból bizonyos helyeken nem lehet érteni, hogy mit is takar az valójában. Fontos szabály, hogy egy név kommunikálja célját, és ne kelljen a kód más részeit átfésülni azért, hogy egy adott változó vagy név jelentését kiderítsük. Ha nem érthető azonnal egy név jelentése, akkor az a név rossz, jobbat kell választani. Az utána következők célravezetőbbek.

Vajon az alábbi kód milyen programban található?

list1 = []
for x in theList:
	if x[0] == 4:
		list1 += x;
return list1

Ha csak annyit változtatunk rajta, hogy értelmes neveket adunk fogalmainknak, már sokkal egyértelműbb a dolog.

flaggedCells = []
for cell in theBoard:
	if cell[STATUS_VALUE] == FLAGGED:
		flaggedCells += cell
return flaggedCells

Igen. Ez egy aknakereső kódjának részlete. De lehet még fokozni egy kicsit.

flaggedCells = []
for cell in theBoard:
	if cell.isFlagged():
		flaggedCells += cell
return flaggedCells

Ez már valóban jól olvasható. Magyar fordításban:

“Iteráljunk végig a tábla összes celláján. Ha egy cella “zászlós”, rakjuk be a tárolónkba, végül adjuk vissza a tárolót a hívónak.”

Kerüljük a félrevezetést

Ne vezesd félre a programozót!

Ha ezt a változót látod valahol egy kódban…

accountList;

akkor joggal várhatod el, hogy az tényleg egy lista legyen. Vagyis a következő függvényeknek az adott objektumra hívhatónak kell lenniük:

  • append()
  • erase()
  • insert()
  • C++ esetében például a sort() is

Így ha a fenti változó például csak egy egyszerű tömböt jelöl, a választott név nem jó. Jobb választás pl. az accounts név.

Hasonló változónevek

A szemünk hajlandó átverni, ha nagyon hasonló neveket használunk. Boldog hibakeresést!

XYZControllerForEfficientHandlingOfStrings
XYZControllerForEfficientStorageOfStrings

Radáusl léetizk egy iylen jleneésg is. Szvóal fnoots, hgoy ne hszalánj hsaolnó szvaakat.

1 szó/fogalom

Egy szót egy fogalommal társítsunk. Egy rossz példa az alábbi.

//1.
 void LoadSingleData();
 void FetchDataFiltered();
 void GetAllData();
//2.
 void SetDataToView();
 void SetObjectValue(int value);

Az első szakaszban a load, fetch, get szavak ugyanarra a fogalomra utalnak, míg a második szekcióban különböző fogalmak vannak azonos névvel jelölve. Ez így nem jó. Helyesen így festene:

//1.
 void GetSingleData();
 void GetDataFiltered();
 void GetAllData();
//2.
 void LoadDataToView();
 void SetObjectValue(int value);

Absztraktság szabálya

Az osztályok neve legyen annyira absztrakt, mint maga az osztály. Például a Protocol név absztrakt osztályt kell takarjon.

Átnevezés szabálya

Tegyük fel, hogy lelkiismeretes programozónk találó nevet adott egy függvénynek. A követelmények változása miatt a függvény törzse is változik, általában többet csinál mint azelőtt. Ilyenkor jó ha újraolvassuk a függvényt hívó kód részletét. Ha nem fedi az újdonsült igazságot, akkor át kell nevezni. Sok debug időt spórolhatunk meg, ha a nevek mindig aktualizálva vannak és az igazságot kommunikálják.

Kiejthető nevek

Vajon mit takarnak a következő nevek?

class DtaRcrd102 {
    private Date genymdhms;
    private Date modymdhms;
    private final String pszqint = "102";
};

Nem jobb így?

class Customer {
    private Date generationTimestamp;
    private Date modificationTimestamp;
    private final String recordId = "102";
};

Mellőzzük a magyar jelölést!

Ezt a jelölést annak idején ifjabbik Simonyi Károly (Charles Simonyi) fejlesztette ki a C nyelvhez. Akkoriban nehéz volt visszafejteni, hogy az egyes változóknak mi a típusa, ezért a változó nevébe kódolta.

Például a m_pszName egy struktúra saját változója volt (m_), ami egy nullvégződésű (z) sztringre (s) mutató pointer (p) volt és minden bizonnyal valamilyen névre mutatott.

Hogy miért is mellőzzük ezt a jelölést? Hát többek között ezért:

#include "sy.h"
extern int *rgwDic;
extern int bsyMac;
struct SY *PsySz(char sz[])
{
	char *pch;
	int cch;
	struct SY *psy, *PsyCreate();
	int *pbsy;
	int cwSz;
	unsigned wHash=0;
	pch=sz;
	while (*pch!=0)
		wHash=(wHash<>11+*pch++);
	cch=pch-sz;
	pbsy=&rgbsyHash[(wHash&077777)%cwHash];
	for (; *pbsy!=0; pbsy = &psy->bsyNext)
	{
		char *szSy;
		szSy= (psy=(struct SY*)&rgwDic[*pbsy])->sz;
		pch=sz;
		while (*pch==*szSy++)
		{
			if (*pch++==0)
				return (psy);
		}
	}
	cwSz=0;
	if (cch>=2)
		cwSz=(cch-2/sizeof(int)+1;
	*pbsy=(int *)(psy=PsyCreate(cwSY+cwSz))-rgwDic;
	Zero((int *)psy,cwSY);
	bltbyte(sz, psy->sz, cch+1);
	return(psy);
}

Ma már olyan fejlesztőeszközök vannak, hogy a változó típusát hamar megtudhatod, ha érdekel. Általában elég csak az egeret a változó fölé húzni és az IDE nagyon szívesen megmondja neked annak típusát.

Nevek és szófajok

Az alábbi táblázat szerint érdemes figyelni a nevek szófajára.

Nevek típusai szófaj példa
Változók, osztályok főnevek Vehicle, customer, employee
Metódusok igék bird.fly(), item.getPrice()
Bool változók, metódusok kifejezések list.isEmpty(),
buffer.hasTask(),
worker.isBusy()
Enum (Státuszt, vagy leírót tartalmaznak) gyakran melléknevek enum Color{Red, Green, Blue}
enum Status{Pending, Closed, Cancelled}

Ha jól figyelsz a szófajra, olvasmányos lesz a kódod.

if(employee.isLate()){
	employee.fire();
}

A rosszul választott szófaj bizonytalanságot szülhet.

if(set(username,"zsolt")){
	...
}

Vajon arra vagyunk kíváncsiak, hogy zsolt a username értéke, vagy beállítjuk zsolt-ra és azt vizsgáljuk, hogy sikeres volt-e a művelet? Ha az if-en belül például az “isSet” kifejezés lenne, fel sem merülne a kérdés.

Scope szabály

Változók

Kis scope-on használt változóknak rövid nevet érdemes adni, nagyobb scope-on használtaknak pedig bátran adhatunk hosszabb, kifejező nevet.

for (Cell cellToEvaluate: getLivingCells()) {
	int numberOfLivingNeighbors = countLivingNeighbors(cellToEvaluate);
	if (numberOfLivingNeighbors == 2 || numberOfLivingNeighbors == 3) {
		ng.add(cellToExamine);
	}
}

A fenti kódban zavaró, hogy nem tudjuk mit is takar az ng változó. Annak deklarációja sokkal feljebb található a kódban. Kevésbé zavaró, de szemet szúrhat, hogy a numberOfLivingNeighbours lokális változó összesen két sorban van használva. Ennek ellenére a neve nagyon hosszú, olvasása kissé kényelmetlen. Hasonló a helyzet a cellToEvaluate névvel is. A kód tisztább verziója így néz ki:

for (Cell cell: getLivingCells()) {
	int count = countLivingNeighbors(cell);
	if (count == 2 || count == 3) {
		nextGeneration.add(cell);
	}
}

A fenti kódrészletet alapján a témában jártas programozó felismerheti a Conway féle életjátéknak részletét.

Függvények, osztályok

Mivel a publikus függvényeket sok helyen használjuk, praktikusabb rövid, találó nevet adni nekik. A privát függvényeink kaphatnak hosszú nevet, ami jól leírja mit is csinál. Általában privát függvényeket amúgy sem hívunk túl sok helyről, nem lesz zavaró a hosszú név.

Az osztályok esetében egy leszármazott neve általában hosszabb elődjénél, hiszen működése sokkal konkrétabb. Ezért a neve is pontosításra szorul. Gyakran ősének valamilyen toldalékolt változata.

Források

  1. Clean Code: Names
  2. Clean Code – A Handbook of Agile Software Craftsmanship — Martin, Robert C. Prentice Hall, 2009
  3. Életjáték
Tiszta kód – 1. rész: nevek

Vélemény, hozzászólás?

Adatok megadása vagy bejelentkezés valamelyik ikonnal:

WordPress.com Logo

Hozzászólhat a WordPress.com felhasználói fiók használatával. Kilépés / Módosítás )

Twitter kép

Hozzászólhat a Twitter felhasználói fiók használatával. Kilépés / Módosítás )

Facebook kép

Hozzászólhat a Facebook felhasználói fiók használatával. Kilépés / Módosítás )

Google+ kép

Hozzászólhat a Google+ felhasználói fiók használatával. Kilépés / Módosítás )

Kapcsolódás: %s