Objektum v. elemi adat?
Azon töröm a fejem, hogy ha van egy osztályom, amelyet adatstruktúraként használok, akkor a benne lévő, logikailag elemi adatokat miképp kezeljem?
(Tiszta kódot olvastam már megint, de nem találom azt a részt, ahol szóba került, hogy egy objektum lehet adattároló is, spec. metódusok nélkül, de akkor csak adatokat publikáljon, vagy lehet olyan, ami pl. üzleti logikát valósít meg, de annak ne legyen publikus változója, csak metódusai)
Valóban legyenek elemi adatok és maga az osztály feleljen azért, hogy csak validált értékek kerülhessenek az egyes változókba?
Vagy az egyes, elemi adatokat tartalmazó változók maguk is objektumok legyenek, amelyek önmagukat képesek ellenőrizni?
Van erre is valamiféle "előírás" az objektum orientált programozással kapcsolatban?
Programnyelvet szándékosan nem említettem, de pl. java-ban, PHP-ben ennek inkább lehet jelentősége, mint egy olyan nyelvben, mint a python v. a ruby, ahol minden objektum, így kevésbé okoz lelki problémát a döntés.
■ (Tiszta kódot olvastam már megint, de nem találom azt a részt, ahol szóba került, hogy egy objektum lehet adattároló is, spec. metódusok nélkül, de akkor csak adatokat publikáljon, vagy lehet olyan, ami pl. üzleti logikát valósít meg, de annak ne legyen publikus változója, csak metódusai)
Valóban legyenek elemi adatok és maga az osztály feleljen azért, hogy csak validált értékek kerülhessenek az egyes változókba?
Vagy az egyes, elemi adatokat tartalmazó változók maguk is objektumok legyenek, amelyek önmagukat képesek ellenőrizni?
Van erre is valamiféle "előírás" az objektum orientált programozással kapcsolatban?
Programnyelvet szándékosan nem említettem, de pl. java-ban, PHP-ben ennek inkább lehet jelentősége, mint egy olyan nyelvben, mint a python v. a ruby, ahol minden objektum, így kevésbé okoz lelki problémát a döntés.
Fogaskerekek
Pl. gondolj az <img> (html) tag-re, mint egy objektumra. (Itt nem a html a legjobb példa, mert ott a böngésző is, de gondold azt, hogy "programnyelv". Delphiben pl. ilyen a TImage komonens - egy osztály.) Nem pusztán adattároló, mert (közvetve) meg is jeleníti a képet, sőt, "kezelni tud" eltérő típusú fájlokat is. Viszont adott esetben néhány mega adatot is tárol közben. Sőt, letölti a kép bájtjait a szerverről, kicsomagolja a tömörített képet, stb.
De ami a lényeg: egy jól körülhatárolt feladatot végez el, mégha ez egy komplexebb feladat is. Ezt úgy tudja megoldani, hogy egyszerre adattároló is és funkcionális is. Ez a legtöbb feladat esetében így van, ezért még nem kell több osztályban megvalósítani.
De azt hiszem ez a "kinek hosszabbak az osztályai" már volt téma. Nem (csak) ezen múlik az OOP.
Változókat ellenőrizni is sokféle módon és helyen lehet, nincs egyetemes "legjobb megoldás", mindig a feladat / programozó függvénye.
Valami félreértés van a
egy osztály lehet adattároló (kb. a C struct-nak v. a COBOL Data Division-ben leírt rekordoknak megfelelőnek képzelem), ilyenkor publikus adattagjai vannak.
PHP-re fordítva az adattároló objektum változóira közvetlenül hivatkozhatsz: $objektum->valtozo.
Ebben az esetben (ha jól értem), az osztály/objektum szerepe kimerül az adatok tárolásában, max. getter/setter metódusai lehetnek.
Illetve lehet hagyományos értelemben vett objektum, de akkor az OOP alapelvek szerint az adattagokat nem publikálhatod (lásd egységbezárás vagymi... ;-) ), kizárólag publikus metódusok segítségével kommunikálhat a "külvilággal".
És akkor ezek keveréke az ActiveRecord, ami aztán végképp bekavart, az említésekor végképp elvesztettem a fonalat. :-)
----
Ami az ellenőrzést illeti, itt most kifejezetten arról beszélek, hogy van egy osztályom/objektumom, amiben adatokat tárolnék, de azt akarom, hogy ezek mind ellenőrzött adatok legyenek. Nem teljes körű, logikai ellenőrzésre gondolok, hanem szintaktikai ellenőrzésre, önmagában értelmezhető értéktartományra stb.)
Bizonyos szempontból azt tartanám jobbnak, ha a elemi adatokat tárolnék és az osztály en bloc ellenőrizné valamennyi bemenő adatát. (mondjuk azért, mert ezek egy adatbázis tábla mezőinek leképezései)
Viszont ha a SRP-t próbálom szem előtt tartani, pontosabban azt, hogy egy osztálynak csak egyetlen oka lehet a változásra, akkor valamennyi benne tárolt "elemi" adatnak önálló objektumnak kell lennie, amely implementálja az ellenőrzéshez szükséges interface-t.
(és ezt meddig lehetne még bonyolítani... :-)) )
PHP OOP...
$objektum->valtozo = valami
máris egy sor hibalehetőséget hordoz, a gyengén típusosság miatt. Így, publikus adattaggal, nem tudod ellenőrizni az adat helyességét. A "getter/setter" metódusok ezt hivatottak megvalósítani. Namost az, hogy az objektumomnak van pár ilyen metódusa, nem jelenti azt, hogy egyéb (pl. "üzleti") logikát nem valósíthat meg. De ilyen logika lehet az is, amikor az adatok összeférhetőségét vizsgálod. Ez pl. egy külön privát függvény, amit minden setter végén / közben meghívsz.Szerintem a feladat / szemlélet dönti el, hogy az adott esetben mikor és mennyire van értelme többfelé bontani az osztályt.
Valóban legyenek elemi adatok
Minden változó maga felel az általa tárolt adatokért, típusától függően. Egy összetett adatszerkezet, mint egy osztály, az alkotóelemei közötti konzisztenciáért felel. Vannak programnyelvek, amik ebben segítenek, és vannak, amik nem. A PHP vagy a Java nem.
Köszi! Szóval bármennyire
Tudnál példát mondani olyan nyelvre, ami segít az adattagok közti konzisztencia ellenőrzésében?
Az érték átadásakor az
http.request
osztálymethod
adattagjának adhatsz értékülGET
-et, de kivételt kapsz a@GET
-re. De hiába adsz neki értékülGET
-et, ha abody
adattagja nem üres, mert akkor arequest
osztály maga fog kivételt dobni.Ahhoz, hogy ez automatikusan megvalósuljon, az értékadás esemény szülőirányú tovaterjedésére volna szükség. Nem ismerek olyan programnyelvet, ami ezt megvalósítaná.
Ennek hiányában a megvalósításnak két útja lehetséges. A szkriptnyelvek általában lehetővé teszik, hogy az osztály elkapja az adattagra vonatkozó értékadást. Ilyenkor az osztályban meghívhatod az adattag – szándékozott – típusához tartozó ellenőrzőfüggvényt, majd ha azon átjut az érték, ellenőrizheted, hogy az osztály többi adattagjával összefér-e.
És vannak olyan nyelvek, amikben az adattag maga végzi az ellenőrzést, amikor értéket kap, azonban az osztály nem veszi észre, hogy egy eleme változott, így ebben az esetben az adatok közti konzisztencia ellenőrzéséhez kell külön függvényt hívnod. Esetleg az adattag tárolhat az őt magába foglaló objektumra hivatkozást, és az értékadáskor kezdeményezheti az ellenőrzést.
Biztosan velem van a baj, de
A másik: a korábbi hozzászólásodban te írtad, hogy
Ezért kérdeztem, hogy tudnál-e konkrét példát azokra, amelyek segítenek...
Biztosan velem van a baj, de
Egy hipotetikus programnyelv hipotetikus HTTP könyvtárát hoztam példának. Feltételezem, hogy ismered a HTTP protokoll működését, így tudod, mi az a
GET
. A@GET
egy szintaktikailag hibás metódusra volt példa.Ehhez a fő gondolathoz tartozott a megjegyzés, miszerint vannak programnyelvek, amik segítenek benne. Bármelyik programnyelv lehet rá példa, amelyik erősen típusos és/vagy lehetővé teszi az értékadás művelet felülírását. A C++ ilyen, a PHP nem.
Az adatok közti konzisztencia ellenőrzésére bármelyik programnyelv jó példa, amelyik lehetővé teszi invariáns definiálását az adatszerkezethez és/vagy az osztály értesítést kap egy adattagja változásáról. A PHP tudja az utóbbit, az Eiffel és a D talán az előbbit, a C++ egyiket sem.
OK, akkor totálisan
Programnyelvek: java7-ben sem lehet felülírni az értékadást? Úgy emlékszem, anno a tervek közt szerepelt valami ilyesmi is... Illetve a másik (még nem néztem utána): a C++ engedi felülírni egy int típusú változó értékadását is?
a C++ engedi felülírni egy
Nem, de írhatsz egy saját egész osztályt. Más nyelvekben pedig rendelhetsz megengedett intervallumot a skalár típusodhoz.
Attól függ
Mivel a legtöbb esetben nem tudni, hogy "100 év múlva" nem lesz-e szükség mégis valahol a "Limited_int"-re, ezért javasolják sokan, hogy mindenképp külön osztályban valósítsd meg.
A Clean Code-ban az a rész
Az adatstruktúrák olyanok, hogy az ő feladatok az adattárolás, és ha módosítani akarod a bennük lévő adatokat, akkor át kell adnod őket olyan függvényeknek, amiknek pont ez a módosítás a feladatuk. Szóval az ő adataiknak a manipulálása kívülről megy. Az objektumoknál az objektum elrejti, hogy milyen adat van benne, az adatok manipulálása pedig belülről; az objektum metódusaival megy.
A könyvben erről van egy csomó példa, többek között olyan is, ami koordinátákkal szemléltet.
vektor adatstruktúrák:
vektor objektum:
Ez a koordinátás példa ezt egy kicsit máshogy mutatja. Tegyük fel, hogy van még egy vektorokat leíró módszer, mondjuk ami komplex számot ad vissza, vagy bármi más (lényegtelen igazából, hogy micsoda). Ha ezt az új típust hozzá akarjuk adni az adatstruktúrás megoldáshoz, akkor létre kell hozni a transformeren egy oda és egy vissza alakító metódust mindkét meglévő típushoz, tehát összesen 4 új metódusra és egy új struktúrára lesz szükség. Ezzel szemben, ha az objektumos módszert követjük, akkor elég 2 új metódus: a tárolási adat formára konvertálás és az onnan vissza konvertálás.
Ha objektumokkal dolgozol, és nem adatstruktúrákkal, akkor ha előjön olyan helyzet, hogy egy objektum egy másik objektum adatait lekéri, manipulálja, majd újra beállítja rajta, akkor az elvi hiba, mert az objektum nem adja ki az adatait manipulálásra, hanem ő maga manipulálja őket. Az adatstruktúráknál persze pont, hogy ez a járható út... Az, hogy neked éppen melyikre van szükséged a te döntésed.
A típusellenőrzést tartalmazhatják az adatstruktúrák, ha setterrel és getterrel oldod meg bennük az értékadást. Az, hogy mondjuk egy szám egy adott tartományba esik e, szerintem nem számít szigorúan típusellenőrzésnek. Ha sokszor ismétlődik ez a fajta ellenőrzés, akkor ki lehet emelni külön osztályba, mint új típust, ha viszont nem gyakori az ilyesmi, akkor nem tartom fontosnak. Szóval ez eléggé helyzet meg mentalitás függő. Én viszonylag toleráns vagyok ilyen téren, nem szoktam rövid kódokat 1-2 ismétlődés miatt kiemelni, mert nem akarok túl általános kódot írni. Clean Code-ban azt írják, hogy már egy ismétlődés elég ok arra, hogy kiemeljed új metódusba vagy új osztályba az adott kódot.
Ha inged...
Azért jobb úgy, mert minél
HA van ismétlődő kód
Ezen kívül van egy olyan hátránya is a - mondjuk így - túlzásba vitt öröklődésnek / származtatásnak, hogy ha vmelyik "alap" osztályodat kell módosítani, akkor adott esetben mehetsz végig az összes gyermekén is, ahol felhasználtad. Persze ez akkor fordul elő, ha az elején b...ál el vmit.
Csak azt vitatom, hogy ez mindig és mindenhol igaz; tisztában vagyok vele, hogy sokszor igaz.
Kíváncsian várom a cikksorozatot, de nem ígérem, hogy nem fogok ott is vitatkozni... :)
Szerk.: ehhez a DNS mittoménmihez nagyon nem tudok hozzászagolni, ez ehető vagy iható? :)
A rossz tervezés nem az
Ha meg módosítani kell valamit, és nem hozzáadni, az meg tervezési hiba.
De amúgy igazad van, nem kell mindig teljesen következetesen az időt ilyen "szépítésre" szánni, határidő közeledtével az ember örül, ha egyáltalán működik/lefordul a cucc. (Ezt hívják Fear Driven Developement-nek :) )
Biztos?
Ezen kívül van egy olyan
Nem hiszem, hogyha ezeket az elveket betartod, akkor muszáj lenne túlzásba vinni az örököltetést. Ez inkább arról szól, hogy sok pici segédosztályod van, de ha küldesz példakódot, ahol szerinted csak így lehet megoldani, akkor szívesen megpróbálom bizonyítani az ellenkezőjét. :-)
Mihez képest
Mivel ez elvi-szemléleti kérdés (és én nem is állítottam semmi olyat, hogy "csak így lehet"), nem tartom hasznosnak a kód-ping-pongot, és lehet, hogy a további vitát sem. Bár a vita olyanformán hasznos, hogy kódolás közben is eszembe jut néha, így segít több-jobb szemszögből nézni u.azt a dolgot, de szerintem soha nem leszek annyira "darabolós gyilkos" (:)), mint te. Egyébként lehet, hogy nincs is olyan sok különbség a nézetünk közt, csak pár árnyalatnyi (nüansznyi?).
Ki tudja :D
Valahol én is egyetértek.
Ki mennyire
A könyvben, ahol beleakadtam
A többire próbálok majd értelmesen reagálni, de egyelőre nehezemre esik felfogni amit írtál...
Egyébként így, hogy végre
Még elolvasok pár könyvet, aztán írok ide clean code-os cikk sorozatot, meg tervben van egy oldal, ahova fel tudom tenni a példáimat. A gondom, hogy refactoring nyomonkövetésére még nem láttam semmi használható megjelenítési módot. Egyedül ilyen van, hogy kivastagozzák a lépéseket, de az leírva elég gáz, mert 200x le kell írni 1-1 kódot, ha kellő részletességgel meg akarja mutatni az ember. Egyelőre arra gondoltam, hogy beszórom egy verziókezelőbe, és minden lépést committálok, aztán majd kitalálom, hogy mi lesz. Pl egy diasorban már elképzelhető, hogy le lehet adni normálisan.