Prefixháború az osztályok elnevezésében

cikk borítókép

Van egyfajta háború az programozói világban az osztályok, interfészek és most már a PHP esetén a trait-ek elnevezését illetően. Igen szélsőséges vélemények tudnak megjelenni, amik nagyon el tudják bizonytalanítani az amúgy is bizonytalan fejlesztőpalántát, de talán még a régebb óta programozókat is. Ahogy az sajnos általában lenni szokott, sokan annyira ragaszkodnak a véleményükhöz, hogy a másik oldal képviselőin már-már szánakoznak, mily balgaságot követnek el. Azt hiszem, itt az ideje egy kissé rugalmasabb megközelítésnek és összehasonlításnak arról, hogy a fenti definíciókat miként érdemes elnevezni, illetve miért nem eredendő bűn egyik vagy másik megoldástól eltérni. A cikkben az egyes csoportok nevei a saját fantáziám szüleményei, hogy megfoghatóbbá tegyem a különbséget.

A két nagy tábor

Azt mondják, ahány ház, annyi szokás. Ez igaz a programnyelvekre is. Az adott nyelven programozók között kialakul egy szokás, ahogy a kódot írják. Ahogy tabulálják, és ahogy az azonosítókat elnevezik. Ahol a nyelvbe beépített osztályok, metódusok elnevezései következetesek, ott ez erősen meghatározza a fejlesztők szokásait is. Nyilván mindent sokféle módon lehet csinálni, de szembemenni a nyelv alkotóival nem szerencsés. Ugyanakkor nincs mindenre határozott recept és a fejlesztőknek még azért megvan a szabadsága, hogy egy általuk jobbnak vélt úton induljanak el. Elvégre a szokások formálják a sztenderdeket, a fejlődésnek pedig utat kell hagyni. Arról viszont megoszlanak a vélemények, hogy mi a jobb. Nézzük a két nagy tábor véleményét!

Jelöld a jellegét

A fejlesztők egy csoportja úgy véli, átláthatóbbá és talán logikusabbá is teszi a kódot, ha az elnevezésben is jelölik egy-egy definíció jellegét. Így ránézésre egyértelmű, hogy egy osztály példányosítható-e vagy csupán egy absztrakt megoldást rejt. Interfész esetén pedig látszik, hogy nem érdemes benne implementációt keresve akár az IDE segítségével a forráskódjához navigálni. Ez így még önmagában persze gyenge érv, de több is van mögötte.

Azt is mondják, hogy nem mindig lehet egyértelmű nevet találni egy definíciónak. Azaz előfordulhat, hogy szükség van egy Animal interfészre és egy absztrakt megvalósításra is. A másik eset, amikor az absztrakt megvalósításból csak egy általános implementációra van szükség egy konkrét programban és a fejlesztő nem akar ezért konkrétabb nevet adni az osztályának. Például, ha egy absztrakt Animal osztályhoz elkészítené az ugyancsak Animal nevű leszármazott osztályt, mivel az ő programjában nincs jelentősége annak, hogy milyen állatról van szó. Ez nyilván akkor fordulhat elő, ha az absztrakt verziót és a konkrétabb definíciót két különböző programozó készítette.

A fejlesztést támogató szoftverek arra is képesek, hogy egy osztály nevét begépelve gépelés közben felajánlják a választási lehetőségeket. Ha tudom, hogy szükségem van egy interfészre, vagy absztrakt osztályra, akkor a kulcsszót begépelve akár csak az absztrakt osztályokat ajánlja fel a szerkesztő. Ráadásul pont a névterek miatt ugyanaz a név több névtérben is előfordulhat, vagyis valamit mégis csak szűr rajta, ha ezt a plusz információt is az osztály nevéhez fűzöm.

Így a "jelöld a jellegét" csoport szerint így kellene írni a definíciókat:

abstract class AbstractAnimal { }
class Dog extends AbstractAnimal { }

vagy

interface AnimalInterface { }
class Dog implements AnimalInterface { }

A trait-ek inkább egy konkrét viselkedést próbálnak leírni, ezért az Animal trait-nek nincs sok értelme, de egy általános, stringgé konvertáló metódussal valahogy így nézne ki a trait elnevezése:

trait StringifyTrait
{
    public function __toString() { /* ... */ }
}
class Dog implements AnimalInterface
{
    use StringifyTrait;
}

Legalábbis a PHP programozók körében népszerűbb ez a megoldás. A Microsoftnál egy interfész esetén például az "I" előtaggal lehet még találkozni. Vagyis az AnimalInterface helyett IAnimal lenne az interfész neve.

Nevezd nevén

A világ a dupla "N" szemüvegen át

A másik tábor szerint az elnevezésekbe nem szabad belekeverni olyan információkat, amik egyébként a szintaktikai elemekből, a definíciókat megelőző kulcsszavakból is kiderülnek. Ilyen például egy osztály absztrakt volta, de ilyen az interface vagy a trait szavak megjelenítése is a nevekben. Ezek ugyanis nem hordoznak valódi plusz információt. Ráadásul könnyen belátható, hogy én is egy ember vagyok és nem pedig absztrakt ember. Ugyanígy nem absztrakt állatokat tartunk, hanem csak állatokat. Ha nem vihetek be állatot egy lakásba, akkor nyilván tudom, hogy kutyát sem, pedig nem használtam az absztrakt szót. Tehát ennek a tábornak a véleménye alapján az elnevezéseinknek az alábbi módon kellene alakulnia:

abstract class Animal { }
class Dog extends Animal { }

vagy

interface Animal { }
class Dog implements Animal { }

illetve

trait Stringify
{
    public function __toString() { /* ... */ }
}
class Dog implements Animal
{
    use Stringify;
}

Azt azért elismerik, hogy bizonyos esetekben indokolt lehet ettől eltérni, de tudni kell, hogy miért térünk el és csak akkor meglépni, ha szükséges.

Ezzel az állítással nehéz vitába szállni. Vagy mégsem?

Válasz a dupla "J"-nek:

Ez a csoport ellenérveket is fel tud sorakoztatni a "Jelöld a jellegét" társaság érveire is.

  • Mióta a komolyabb IDE-k a forráskódot feltérképezve a kulcsszavak alapján külön ikonokkal segítik a fejlesztőt, szerintük felesleges redundánssá tenni a kódot az ilyen magyarázó elnevezésekkel. Egyesek konkrétan csak "a hülye Magyar jelölésnek" nevezik külföldi fórumokon.
  • Manapság, a névterek használatának korában annak nem nagy a realitása, hogy egy másik fejlesztő által írt osztály neve megegyezik a sajátommal. Sőt, szükség esetén át is lehet nevezni bármelyik osztályt:
    use Animal as AbstractAnimal;
  • Ha egy absztrakt osztály megvalósításának nem tudunk új nevet kitalálni, még mindig lehet csak ebben az esetben egy "Impl" jelöléssel megtoldani, ami egy bevett szokás a Java nyelvben is. Vagyis class AnimalImpl extends Animal { } lenne a helyes verzió.
  • Az IDE-k annyira okosak, hogy az implements kulcsszó után csak interfészeket ajánlanak fel, az extends után pedig csak osztályokat. (Igaz, nem csak ezek után lehet szükség egy osztály nevének beírására.)

Kicsit távolabbról szemlélve

Valójában a Magyar jelölés, vagy angol nevén a Hungarian notation, Simonyi Károly fiához, Charles Simonyi nevéhez fűződik, aki az általa kifejlesztett módszerről cikket is írt. Ez még nem az objektumorientált szemléletről szólt, hanem leginkább a C nyelvről és ott is inkább a változókról és eljárásokról. Ennek ellenére abban lehet valami, hogy aztán ebből fejlődött ki az "I" prefix az MS-nél az interfészekre. Ennek történetét nem ismerem.

A szabály célja viszont az volt, hogy adott körülmények között a kód mégis olvasható legyen. Azért itt a rövidítések miatt inkább a szabályokat jól ismerők számára lehetett ez csak előny. A cél viszont ma is megmaradt, csak más eszközökkel. A rövid elnevezéseket felváltották a hosszabb, kifejtősek, hiszen az IDE úgyis befejezi helyettünk és ismeretlenek számára is olvasható marad a kód. Az eljárások helyett már metódusokról beszélünk, de azok nevei továbbra is gyakran tartalmaznak utalást a paraméterükre. Főleg akkor, ha nem egy olyan nyelvről beszélünk, mint a Java vagy a C#, ahol létezik az úgynevezett "overloading", ahol a várt paraméter típusa is megkülönböztet két metódust így ugyanazt a nevet kaphatják.

Egyet tudok érteni a "Nevezd nevén" szimpatizánsainak azzal a véleményével is, hogy inkább következetesen alkalmazzák az elnevezéseket ahelyett, hogy időnként az alapfelállástól eltérjenek. Mivel ez nem okoz nagy sebet a kódban, tulajdonképpen ez is elfogadható álláspont.

Természetesen az ellenkező véleményen levők sem a következetlen kód mellett voksolnak, csak máshol húzzák meg a határt. Náluk a mérleg más fele billen és ez is teljesen érthető. Abban is mindkét fél egyetért, hogy a választott konvenciótól csak erősen indokolt esetben szabad eltérni. Ha az ember attól függően írja a kódját, hogy milyen lábbal kelt fel reggel, akkor lehet, saját maga alól rúgja ki a széket.

Viszont némelyik érv úgy érzem, nem áll meg a lábán. Egy programban, főleg OOP-ben a való világot igyekszünk lemodellezni, de a modell nem arról szól, hogy egy az egyben megfeleltetünk. A való világban az állat nem absztrakt állat, de nem is absztrakció és nem is API definíció, hanem inkább gyűjtő fogalom. A programozásnál szükségünk van olyan eszközök használatára, ami a valóságban nem jelenik meg, illetve egyes részleteket figyelmen kívül hagyunk, mert nincs jelentősége a program szempontjából.

Vagyis az elsődleges szempont az marad, hogy mi bizonyul hasznosabb gyakorlatnak, mi segíti leginkább a hatékony fejlesztést. A válaszról persze még így is lehet vitatkozni.

A bírók döntöttek

Megmondom őszintén, én mindkét csoportnak voltam már tagja. Pont azért, mert mindkettőnek megvannak a maga elfogadható érvei. Én pedig igyekeztem követni a nálam okosabbakat, akik viszont maguk sem voltak ugyebár egymással egy véleményen. A több programnyelv ismerete pedig szintén csak összezavart az ellenkező szokások miatt.

Aztán viszont megjelent a PSR ajánlás és amiben lehetett, örömmel elkezdtem követni az ajánlásokat. Ugyan az osztályok, interészek, trait-ek jelöléseiről nem szól, de a saját fejlesztéseikre, példáikra létezik egy elnevezési konvenció, ami úgy tűnik, a "Jelöld a jellegét" pártját fogja. Ez a tény, és hogy sok keretrendszer is ezt az utat követi, mint a Symfony vagy a Zend 2 (A Zend 1 is így tett, csak kicsit másképpen) engem meggyőzött és ismét csak követem a nagyokat.

Ha valaki mégis úgy érzi, hogy ezzel nem tud azonosulni, szíve joga. Ne érezze magát ettől kellemetlenül! Mégis bölcs dolognak tartanám a nyelven belül egy közös konvencióra törekednénk és az előttünk, a nagyok által kitaposott út jó választásnak tűnik.

Hogy ezek után én melyik csoportba tartozom? Leginkább egy harmadik, a "Kövesd az ajánlást" csoportba. Ez viszont épp egyezni látszik a "Jelöld a jellegét" elveivel.

Kategóriák: 
Megosztás/Mentés