ugrás a tartalomhoz

Jó az, ha a leszármazott más típussal tér vissza, mint a szülő?

H.Z. v2 · 2011. Júl. 2. (Szo), 20.50
Okoz-e valakinek lelki sérülést egy olyan húzás, ha a szülő osztály egy metódusa más típusú értéket ad vissza, mint a leszármazotté? :-)
(szóval, hibának számít-e, ha ilyet teszek?)

Visszatértem az alapokhoz és újra 0-ról próbálom felépíteni a kis oldalamat. Itt kaptam valakitől az ötletet, hogy a rekordok kezelésére nem ártana egy absztrakt osztály, aminek a kiterjesztésével hozhatom létre a tényleges adatokhoz szükséges osztályokat.

abstract class RecordHandler {
    protected $buffer=array(); # Ide kerülnek a beolvasott adatok
                               # Egy tömbelem=egy sor, minden sor egy-egy tömb,
                               # melyben egy-egy elem=a sor egy mezője

    ...
    public function getData(){ 
      ... 
      return $buffer;
    }
    ...
}

# ez egy olyan osztály, amely biztosítja, hogy az objektum csak akkor jöjjön
# létre, ha a létrehozásához megadott kulcshoz max. 1 sor/rekord tartozik
# a tárolóban (ami nem feltétlenül RDBMS, ezért a körülményes megfogalmazás)
abstract class UniqRecordHandler extends RecordHandler {
   ...
   public function getData(){
      ...
      return $buffer[0];
   }
}
Ez így mennyire gázos?
Normális körülmények közt a fenti szülő és leszármazott működése csak annyiban tér el egymástól, hogy a gyerek osztály végrehajt egy ellenőrzést a beolvasott adatokon és exceptiont dob, ha egynél több sort talált a kulcsához. Ha nem írom felül a getData-t, akkor a visszaadott értékre mindig kétdimenziós tömbként kellene hivatkozni, ami kényelmetlen lenne.
De egyáltalán az, hogy az egyedi kulcsból létrejövő objektumokat külön kezelem, ez mint elképzelés elfogadható?
(RDBMS-t használva nincs sok értelme, hiszen ráhúzok a megfelelő mezőre egy unique key-t és nem kell tovább foglalkozni vele, de gondolok arra az esetre, ha mondjuk rámjön a hülyeség és mondjuk CSV file-t akarok "adatbázis" helyett használni)
 
1

Csinálj first és last

inf · 2011. Júl. 2. (Szo), 21.50
Csinálj first és last metódusokat, bármelyikkel kikérheted az első sort... :-)
A lényeg mindig, hogy egyértelmű legyen a metódus nevéből, hogy mit kapsz tőle, ha úgy csinálod, hogy egyszer egy recordot, másszor meg egy tömböt ad recordokkal, akkor az nem egyértelmű, szóval szerintem gáz.

"Jó az, ha a leszármazott más típussal tér vissza, mint a szülő?"
Erre válaszolva: helyzet függő. A típus jelen esetben meg ugyanaz, mivel mindkettő tömböt ad vissza, csak az egyik két dimenziósat...
2

Köszi. Ami az eltérő típust

H.Z. v2 · 2011. Júl. 3. (V), 09.07
Köszi.

Ami az eltérő típust illeti: "fizikailag" valóban mindegy, tömb-tömb mindkettő.
De logikailag az egyik dimenzió egy rekordszerkezet, csak ezt a fogalmat a PHP nem ismeri. A szülő egy meghatározatlan típusú rekordokat tartalmazó tömböt ad vissza, míg a leszármazott azonos metódusa magát a rekordot.

Ha jól értem, akkor nem célszerű olyan metódusokat készíteni, amelyeknek nem egyértelmű a kimenete -> jobb, ha a Uniq* osztály kap egy másik metódust, ami a rekordot dobja vissza és megtartja az eredetit, ami ugyanezzel tér vissza, de egy tömb 0. elemeként. (azzal nem akarok foglalkozni, hogy egyenként visszaadogassam a beolvasott rekordokat, mert feleslegesen elbonyolítaná az életem)
3

Jól érted.

inf · 2011. Júl. 3. (V), 10.06
Jól érted.
4

Az öröklődésben az a pláne,

tgr · 2011. Júl. 3. (V), 12.14
Az öröklődésben az a pláne, hogy a szülő osztályt kezelő kódnak bármikor átadhatod a gyerek osztály egy példányát is, és azzal is ugyanúgy működni fog. Ehhez azonos típusú (vagy mindenesetre azonosan viselkedő, szigorúbban típusos nyelvben pl. ugyanazt az interfészt megvalósító) visszatérési érték kell. Persze a konvenciók azért vannak, hogy a programozót szolgálják és nem fordítva, de alapos ok nélkül nem rúgnám fel.

De ez az egész architektúra nem tűnik szerencsésnek, hacsak nem tudod egyértelműen a tároló típusához kötni, hogy egyediek-e benne a rekordok vagy sem. Pl. ha van egy CSV fájlod nem egyedi kulcsokkal, a RecordHandlerből leszármaztatsz egy CsvRecordHandlert, abban megírod a CSV kezelés alacsonyszintű részleteit, aztán jön egy másik CSV egyedi kulcsokkal, akkor választhatsz, hogy a UniqueCsvRecordHandlert a CsvRecordHandlerből vagy a UniqueRecordHandlerből származtatod, és a másik kódját duplikálnod kell benne.
5

...

H.Z. v2 · 2011. Júl. 3. (V), 12.42
Az öröklődésben az a pláne, hogy a szülő osztályt kezelő kódnak bármikor átadhatod a gyerek osztály egy példányát


Ez folyton kimarad... (értsd: elfelejtem, pedig néhányszor már felhívták rá a figyelmem :( )

De ez az egész architektúra nem tűnik szerencsésnek, hacsak nem tudod egyértelműen a tároló típusához kötni, hogy egyediek-e benne a rekordok vagy sem.

Ilyenek miatt futok neki az egésznek már sokadszor.
És nem utoljára. :(
Legutóbb pont azon akadtam el, amit te is írtál, hogy duplikálni kellene a kódot bizonyos esetekben, az viszont nem túl jó ötlet...
6

Ha a projekt végén nem érzed

tgr · 2011. Júl. 3. (V), 15.09
Ha a projekt végén nem érzed úgy, hogy ki kéne dobni az egészet és előről kezdeni, nem is vagy igazi programozó :-)
7

Haha +1 :-)

inf · 2011. Júl. 4. (H), 00.40
Haha +1 :-)
8

És ha már az elején ezt

H.Z. v2 · 2011. Júl. 4. (H), 12.28
És ha már az elején ezt teszem? ;-)
Több, mint fél év alatt nem jutottam el odáig, hogy legyen egy bejelentkező képernyőm. :-)))
----------------------------------------------------------
És minek nevezitek azt, aki feltalálja a kereket?
Optimistic Concurrency Control témában keresgélve jutottam el a Propel oldalára és... rá kellett jönnöm, hogy ORM-t írok. :-D
9

Nyugi, pár hónap / év, és

inf · 2011. Júl. 4. (H), 15.45
Nyugi, pár hónap / év, és kinövöd ezt a fázist. Arra figyelj, hogy egy-egy ilyen saját meló ne húzódjon el fél-egy évnél tovább, mert a végén még beleragadsz...

Meg érdemes lenne elolvasnod egy-két komoly könyvet a témában, hogy legyen rálátásod, hogy mi az, ami már készen van, és csak meg kell tanulnod használni...
10

Ehh... bocs, azt hittem,

H.Z. v2 · 2011. Júl. 4. (H), 15.59
Ehh... bocs, azt hittem, érzékelhető a... nem is tudom, minek nevezzem: önirónia?
Csak röhögni tudok magamon: előszedtem a régi, már lezárt blogom és utánaszámoltam, hogy december óta nyűglődök ezzel a hülyeséggel és nem jutok egyről a kettőre. Időkitöltésnek használom, hogy ne öljön meg az unalom, ugyanakkor nem érdekel eléggé ahhoz, hogy komolyan elmélyedjek benne.
Már nem fogom kinőni.

A kerék feltalálásával kapcsolatban azért vigasztal, hogy annyira nem indulok rossz úton amikor a maradék agysejtjeimet próbálom munkára fogni, elvégre mások már előttem kitalálták és nekik működik is. :-))

---
Ettől függetlenül, ha tudsz ajánlani szakirodalmat, azt bármikor szívesen...
Most egy PHP és MySQL webfejlesztőknek, Hogyan építsünk webáruházat c. könyv van mellettem a szekrényen, de ez inkább csak arra jó, hogy összezavarjon sokmindent.
(nem a könyv hibája, csak ők más utakon járnak, mint én)
14

The only valid measurement of

H.Z. v2 · 2011. Júl. 6. (Sze), 17.27
The only valid measurement of code quality: WTFs/min

:-)

Köszi.
15

Látom a lényeget te is

inf · 2011. Júl. 6. (Sze), 17.31
Látom a lényeget te is kiemelted :D
16

De milyen igaza van! :-)

H.Z. v2 · 2011. Júl. 6. (Sze), 17.33
De milyen igaza van! :-)
17

Hát jó, hogy igaza van, ez az

inf · 2011. Júl. 6. (Sze), 17.35
Hát jó, hogy igaza van, ez az egyik legkomolyabb szoftverfejlesztős könyv...
18

A 11.-re nincs ötleted addig

H.Z. v2 · 2011. Júl. 6. (Sze), 17.43
A 11.-re nincs ötleted addig is?
11

Valami mégsem tiszta. Mi

H.Z. v2 · 2011. Júl. 5. (K), 09.26
Valami mégsem tiszta.
Mi értelme a felülírható/-definiálható metódusoknak ilyen formában?
Hiszen, ha tartom magam ahhoz, hogy a leszármazott objektumnak akkor is helyesen kell működnie, ha olyan helyen használom, ahol a szülőre van szükség, akkor gondot okozhat a felülírt metódus eltérő működése is...
Mit értelmezek rosszul?
19

Én nem tudom :-P Lehet, hogy

inf · 2011. Júl. 6. (Sze), 18.03
Én nem tudom :-P
Lehet, hogy a single responsibility-t nem tartod be, például.

Hiszen, ha tartom magam ahhoz, hogy a leszármazott objektumnak akkor is helyesen kell működnie, ha olyan helyen használom, ahol a szülőre van szükség, akkor gondot okozhat a felülírt metódus eltérő működése is...


Az lehet a probléma, hogy nem interface-ekben gondolkodsz, hanem osztályokban. Ha interface-t vársz el, akkor az adott felületet mindkét osztálynak meg kell valósítania, szóval mindkettő működni fog az adott környezetben. Ennyi.
20

Van benne valami (hogy Vágó

H.Z. v2 · 2011. Júl. 6. (Sze), 18.36
Van benne valami (hogy Vágó Istvánt idézzem ;-) )
Leginkább valami makróféleségként kezelem az osztályokat, de...
Az továbbra is zavar, hogy egy örökölt metódus felülírásának csak akkor van értelme, ha az valamit hozzátesz a szülő metódusának működéséhez. Ettől kezdve viszont semmi biztosíték nincs rá, hogy azonos inputból azonos outputot fog produkálni -> ettől kezdve viszont nem feltétlenül lehet a szülő helyére írni a leszármazottat. Na ezen még rágódnom kell egy sort.
--------
update: végiggondolva amit itt leírtam, végülis hülyeséget beszélek. Amin most "rugózok", az pont arra példa, hogy azonos szülő, azonos metódus, eltérő működés, de azonos végeredmény: Szülő absztraktként megfogalmazva, a leszármazottak eltérő adatforrásokból dolgoznak, de a visszaadott eredmény minden esetben egy-egy rekord tartalma. Illetve az update/delete műveleteknek is azonos eredménye kell, hogy legyen.
21

Nyilvánvaló; ha eltérő lenne

inf · 2011. Júl. 6. (Sze), 22.09
Nyilvánvaló; ha eltérő lenne a végeredmény, akkor más lenne a metódus neve...
22

Erre reggel még volt egy jó

H.Z. v2 · 2011. Júl. 6. (Sze), 22.46
Erre reggel még volt egy jó ellenpéldám, csak nem írtam fel és már nem bírom újra összehozni. :-(
Valami olyasmin gondolkodtam, amit triviális példaként szoktak hozni az OOP oktatásakor:

class jármű { ... function mozgás(){ ... } }
class bicikli extends jármű { ... }
class repülőgép extends jármű { ... }
class hajó extends jármű { ... }

Mozgásra mindegyik képes, de ugye egyáltalán nem mindegy, hogy hol, hogyan, merre, milyen közegben. Ráadásul itt a jármű osztály mozgás metódusának inkább absztraktnak illene lenni, mivel azt nem lehet egyértelműen a jármű fogalmához rendelni. Igaz viszont az is, hogy visszatérési értékként nehezen tudnék kiötleni olyan adatot, ami értelmes és mégis függ attól, hogy miféle járműről van szó... Szóval már nem tudom reprodukálni.

update: jut eszembe, talán az üzemanyagszint mérő metódus lehetett és pl. gőzmozdony lett volna az egyik jármű? Mert ugye ott még a mértékegység is más lenne...
23

Hát ezek a példák nagyon nem

inf · 2011. Júl. 7. (Cs), 10.26
Hát ezek a példák nagyon nem stimmelnek...

Mindegyik jármű x,y,z irányban tud mozogni.
Az üzemanyagokat meg leadott teljesítményben lehet mérni, ha mindenképpen valami közös jellemzőt akarsz keresni, de ez egyáltalán nem muszáj...
24

Liskov-féle helyettesítési elv

tgr · 2011. Júl. 9. (Szo), 15.42
Az OOP formálisabb meghatározásaiban Liskov-féle helyettesítési elvnek hívják ezt a tulajdonságot (Liskov Substitution Principle), ezen a néven jól guglizható.
12

Még mindig...

H.Z. v2 · 2011. Júl. 6. (Sze), 16.58
Még mindig ezzel az osztállyal nyűglődök, persze megint elméleti gondom van:
Maga az alap, egy absztrakt osztály lenne, ami független az adattárolótól (mindegy neki, hogy adatbázis, soros file, XML stb.), de néhány alapművelet már definiálva van (pl. hogy adom vissza a rekord tartalmát, az update jellegű műveletek körüli ellenőrzések egy része stb.)
Ehhez kapcsolódna leszármazottként egy osztály, amely már tisztában van vele, hogy mondjuk egy MySQL adatbázissal kell kommunikálnia.

Eleve nem tudom, jó irányba megyek-e. Példaként: van egy absztrakt RecordHandler és van egy UserData extends RecordHandler. A UserData már tudja, hogy egy MySQL van mögötte, ennek megfelelő metódusokkal írja felül a RecordHandler absztrakt metódusait, amelyek a fizikai olvasást/írást definiálják.

Ha ezzel nem mentem tévútra, akkor a következő kérdés: általában jó, ha egy weblap letöltésekor csak egyszer konnektálok az adatbázishoz. Honnan vegye az említett UserData az adatbázis objektumot (szándékaim szerint egy PDO objektum)? Minden ilyen objektum próbáljon kapcsolódni önmaga? Csináljam amit eredetileg: a lap elején egy globális változóba rakom az adatbázis objektumot és az őt használó objektumok konstruktorának adjam át? Berakhatom esetleg a RecordHandler konstruktorába, hogy paraméterként elvárja az adatforrás objektumát? Egyéb? (azt nem tartom jó ötletnek, hogy az említett globális változóra közvetlenül hivatkozzak)

-----
Alapvető ismerethiányban szenvedek, de nem találok a neten olyan irományt, ami segítene :-(
(forráskódok egy része túl bonyolult ahhoz képest, amit én akarok, a maradék meg lényegesen egyszerűbb... de még nem adtam fel teljesen)

update: de az is lehet, hogy tényleg nem kéne ilyen komoly agymunkával erőlködnöm. Most találtam pár magyar nyelvű lapot, ami az OOP alapjairól szól (köztük weblaborost is), de a fenti kérdésekre továbbra sem találok egyértelmű válaszokat.
25

A két szokásos megoldás erre

tgr · 2011. Júl. 9. (Szo), 15.45
A két szokásos megoldás erre a singleton (gyakorlatilag egy globális változó, csak rugalmasabb és biztonságosabb) és a dependency injection (minden objektum megkapja a függőségeit, akár a konstruktorban, akár utána egy setXXX hívással - ez pl. akkor jó, ha teszteléskor le akarod cserélni az adatbáziskapcsolatot valami általad kontrollált dologra).
26

Köszi szépen. Singleton

H.Z. v2 · 2011. Júl. 9. (Szo), 15.56
Köszi szépen.

Singleton ügyben folyton összekavarodok: ez valójában egy olyan osztály, aminek csak egyetlen objektuma lehet és erről gondoskodik is, pl. azzal, hogy egy statikus változóban tárolja önmaga egyetlen példányát, amit egy statikus metódusból ad vissza, miközben a konstruktora privát?
Magyarán az általad említett globális változó valójában az osztály statikus változója.
27

Pontosan.

tgr · 2011. Júl. 9. (Szo), 19.11
Pontosan. Annyi az előnye a globalhoz képest, hogy egyrészt első híváskor tudod inicializálni (és így ha semmi nem használja, akkor nem kell, ami pl. egy adatbáziskapcsolatnál már nem elhanyagolható időmegtakarítás), másrészt globalnál előfordul, hogy két különböző programozó (vagy akár ugyanaz a programozó két különböző helyen) véletlenül ugyanazt a nevet választja két különböző dologra, és az így keletkező hibákat roppant nehéz megtalálni, míg ha egy osztályt próbálsz tévedésből kétszer megírni, az elég feltűnő.

Másrészt a teszteléscentrikus metodológiákban nem szeretik, mert a statikus hivatkozás miatt nehéz elválasztani a hívó osztálytól.