Megnéznétek, hány hibát követtem el?
Megint nekiestem a PHP+OOP "tanulásnak".
A mostanában tanultak alapján összeraktam egy kezdetleges kódot, aminek a feladata az lenne, hogy "valahonnan" kiolvassa az alkalmazás működéséhez szükséges paramétereket és ezeket elhelyezze egy tömbben.
A "valahonnan" elméletileg lehetne akár hagyományos ini fájl (csak ez készült el), de elképzeléseim szerint mind a formátum (pl. ini helyett XML), mind az adatforrás (pl. fájl helyett adatbázis) szabadon változtatható.
Ehhez szerettem volna felhasználni a dependency injectiont is, nem tudom, ez mennyire sikerült.
Ha van kedvetek, türelmetek átnézni kb. 80 sort: http://pastebin.com/adruq7y9
Minden kritikát szívesen fogadok.
(azt is, ha egyértelmű, hogy totál hülye vagyok hozzá, mert akkor nem erőltetem tovább ;-) )
ui: kommentek azért nincsenek, mert az olvastam a Tiszta kódban, hogy ha egyébként olvasható a kód, eléggé beszédesek a használt nevek, akkor a megjegyzés csak nehezíti az olvasást. Hát szót fogadtam neki... :-)
■ A mostanában tanultak alapján összeraktam egy kezdetleges kódot, aminek a feladata az lenne, hogy "valahonnan" kiolvassa az alkalmazás működéséhez szükséges paramétereket és ezeket elhelyezze egy tömbben.
A "valahonnan" elméletileg lehetne akár hagyományos ini fájl (csak ez készült el), de elképzeléseim szerint mind a formátum (pl. ini helyett XML), mind az adatforrás (pl. fájl helyett adatbázis) szabadon változtatható.
Ehhez szerettem volna felhasználni a dependency injectiont is, nem tudom, ez mennyire sikerült.
Ha van kedvetek, türelmetek átnézni kb. 80 sort: http://pastebin.com/adruq7y9
Minden kritikát szívesen fogadok.
(azt is, ha egyértelmű, hogy totál hülye vagyok hozzá, mert akkor nem erőltetem tovább ;-) )
ui: kommentek azért nincsenek, mert az olvastam a Tiszta kódban, hogy ha egyébként olvasható a kód, eléggé beszédesek a használt nevek, akkor a megjegyzés csak nehezíti az olvasást. Hát szót fogadtam neki... :-)
Nem
Van benne valami...
Nem tudnám szó szerint idézni, de kifejezetten olyan megfogalmazásra emlékszem, hogy a jó kód olvasható kommentek nélkül is és csak ha nagyon muszáj, akkor írjuk le, hogy mit, miért. Mert normálisan olyan kell legyen, mint egy regény, amit valamely informatikában használatos nyelven írtak.
(ha nem felejtem el, megkeresem és bemásolom ide - nekem kicsit égnek állt a hajam, amikor olvastam)
Megjegyzést ott kell írnod,
Nagyjából jó, pár dolog:-
- Használj interface-eket absztrakt osztályok helyett!
Egy csomó felesleges absztrakt osztályt létrehozol csak validálási céllal...
- Az elnevezések jók, ami fura az a ParametersFromFile.
Az ilyesmiket inkább Loader-nek vagy Reader-nek hívják, mert az sokkal jobban elmondja, hogy mit csinálnak. A ParametersFromFile egy magas absztrakciós szintű elnevezés, viszont az osztályban egy alacsony absztrakciós szintű kód van, aminek egyáltalán nem kell tudnia arról, hogy ő most paramétereket olvas fájlból, vagy dalszöveget, csak annyit, hogy valami adatot ... Amelyik osztályod tudja, hogy paramétereket olvasol be, csak ott nevezheted el ezt paraméter beolvasónak...
- Használj dependency injection container-t paraméterezéshez!
Így nem kell az ApplicationSetup példányosításnál azzal törődnöd, hogy a .ini fájlhoz IniParser kell, stb... Ami a config olvasónál automatizálható, azt meg tudod oldani DIC-vel, és csak azokat a paramétereket kell megadnod a végén az ApplicationSetup-nak, amire tényleg feltétlen szüksége van.
Dependency injection-nel ez valahogy így nézne ki:
szerk:
Hmm a ConfigReader elnevezés helyett valami olyasmi lenne beszélőbb, hogy SerializedDataReader, de nem igazán találtam meg a megfelelően tömör kifejezést...
Köszi!
<defense mode>
Neveket sohasem fogom úgy kialakítani, hogy elfogadható legyen.
Hibakezelés meg azért nem volt, mert ezt csak egy "csontváznak" szántam, hogy az elvekből megértettem-e valamit
</defense mode>
Interface-ek kapcsán... Úgy emlékszem, a PHP megengedi, hogy absztrakt osztályokban nem absztrakt metódusokat is létrehozzak. (mintha pl. a Java ezt tiltaná)
Azért nem használtam interface-t, mert ne legyen szükség duplázni bizonyos dolgokat.
De ha jól értem, akkor az ilyesmit inkább úgy, hogy a közös, azonosan működő metódusokat egy ősosztályba tenni és attól örökölni, amit meg absztrakt metódusként írnék, azt interface-ből átvenni?
Jó az absztrakt osztály, ha
abstract vs interface
Gyanítom, mellényúltam, mert ez így olyan, mintha magamnak válaszoltam volna. :-)
------------------------------------------------------------
Nagyon egyszerű. Egy osztálynak csak egy őse lehet, viszont annyi interfacet implementálhat, amennyit nem szégyellsz...
Amit nem értek a tiédből....
Nálad csak az .ini fájl nevét adhatom meg. Ez nem jelent problémát/valamilyen OO elv megsértését?
Vagy pont azzal sértem meg, ha kívülről akarom befolyásolni pl. azt, hogy a paraméter olvasóm milyen parsert használjon?
(itt kezdek elvenni a felelősségi körök, egységbe zártság és hasonló témákban)
Nálad csak az .ini fájl nevét
Jobb példát írni, mint magyarázni...
Tegyük fel, hogy megadod az XML kódot, amiből be akarod parsolni a config-ot (szóval nem fájlból akarsz parsolni...
Reader vs. Parser?
A ConfigReaderContainerInterface-nek más felhasználása van, mintsem a ConfigFileReaderContainer –t kiegészítse? Elsőre nem látszódik, hogy hol segít? Miben lenne más, ha az ApplicationSetup::configure egy ConfigFileReaderContainer –t várna paraméterben?
A ConfigResourceReader, aki maga is egy ReaderInterface van egy property-je, ami szintén ReaderInterface? Vagy valamit félrenézek? Ez nekem nem annyira érthető, miért kellett így csinálni?
Illetve a ParserInterface az egyben ReaderInterface? Ez sem egyértelmű elsőre, hogy miért kell? Főleg, hogy a IniParser-nek jelenleg nincs read metódusa, ami fordítási hiba.
Kösz, javítottam, a Parser az
A ContainerInterface azért kell, hogy ha máshonnan akarsz beolvasni (ConfigMemoryReaderContainer, ConfigCosmicEnergyReaderContainer etc.) adatot, akkor arra is tudj egy külön container-t csinálni. Mindenképp kell egy másik interface a ReaderInterface-en kívül, ami biztosítja, hogy a setup valid Reader-t kap, mivel a ReaderInterface túl általános. Pl egy FileReader az nem lenne valid, mert szöveget ad át a paraméterek tömbje helyett...
Nem muszáj így csinálni, nekem egyszerűen így volt logikus. Mindkettő adatot olvas be, annyi a különbség, hogy a Config-nál még utánatettem egy parser-t, de attól még ugyanúgy adat beolvasás annak is a célja.
DI
Amivel nekem a kódban bajom van, az az, hogy a legritkább esetben van arra szükség, hogy több féle formátumból tudjon egy alkalmazás konfigurációt olvasni. Sőt, kontraproduktív, mert mindegyikhez kell megfelelő dokumentációt írni. Innentől kezdve én azt mondanám, hogy az alkalmazás írja elő, mit szeretne.
Ami az interfaceket illeti, az előttem szólónak igaza van. Ha le akarsz írni egy szabványt, használj interfacet. Ha tényleg logikailag közös őse van két osztálynak, akkor legyen absztrakt ősosztály.
Amivel nekem a kódban bajom
Ezzel egyet értek. Úgy értelmeztem a feladatot, hogy általános kódra van szükség. Ha én csinálnám magamnak, akkor egy sima fájl olvasó meg parser lenne, mert mindig fájlból szedem a config-ot. Egyébként nagyon könnyű abba a hibába esni, hogy túl általános kódot ír az ember, én inkább úgy szoktam, hogy megnézem, hogy az adott pillanatban mire van szükség, azt megcsinálom, aztán ha valamikor előfordul, hogy mondjuk nem csak fájlból akarok olvasni, akkor az előző változatot módosítom úgy, hogy az új elvárásoknak is megfeleljen. A túl általános kód egy csomó plusz munkával jár, ami az esetek nagy részében nem térül meg.
DIC és egyebek...
Viszont a dologhoz hozzátartozik, hogy ez az egész ama bizonyos állatorvosi lónak a tipikus esete: megpróbálom valóra váltani mindazt amit a már többször említett, Tiszta kód c. könyvben olvastam. Kommentek, elnevezések és ebben az esetben a kód újrahasznosíthatóság.
Egyetlen alkalmazásban tényleg ritkaság, hogy hol .ini-ből, hol .xml-ből, hogy egyéb akármiből kell felparaméterezni magát az applikációt, de... Ezt egyszer megírom és csak a parsert kell cserélni, ha máshol mást kell alátolni.
Ez volt az eredeti elképzelésem.
Hogy mi lett belőle... az más kérdés.
Tiszta kód
Merthogy, nagyon könnyű rossz kommentet írni, és erre vannak a könyvben példák, de nem azt írta, hogy egyáltalán ne írjunk kommentet.
Illetve ott is van egy ellentmondás, hogy a sok kicsi osztály akkor kell, amikor valóban sok kis feladat van. De ha csak egy kis feladat van, akkor nem kell felesleges osztályokat gyártani.
félreértés
Pontosabban "ész nélkül" követve azt, amit ott leírtak, kb. ez lesz a végeredmény.
(bevallom, nem tetszik a szerző stílusa... Van benne valami... nem tudom megfogalmazni, de valami nagyon zavaró.)
Re: DI
Ez a megjegyzésed csak erre a konkrét példára vonatkozik, vagy általánosságban a DIC-et nem támogatod?
Na erre én is kíváncsi
Nézzük
De természetesen FIXME, ha tévednék.
Hát lehet, hogy az én
Itt egy kicsit máshogy írja a Potencier. Ezek alapján nekem az jött le, hogy a DIC-nek az a feladata, hogy a konfigja alapján objektumokat szolgáltasson, nem pedig az, hogy gyűjteményként viselkedjen, szóval hogy az egyik oldalán megadsz objektumokat, a másik oldalán meg kiveszed belőle ugyanazokat. A DIC maga példányosít, így tudja garantálni, hogy amit mondjuk a getDatabase visszaad az PDO osztályú, stb... Persze lehet, hogy én vagyok a hülye :D
OK
A string alapján lekérés
Radikális
Nem hiszem, hogy annyira
Konkrétan
Én inkább a sima dependency injection híve vagyok. Igaz nem olyan univerzális megoldás, de egyből látod, milyen osztály van ott, működik a kódkiegészítés is és nem is kell varázsolni.
Az elnevezések kicsit
Az ApplicationSetup konstruktora nemtriviális munkát végez, ami dependency injectionnél problémákat okozhat. Egyáltalán, miért a paramétereket tároló/visszaadó osztály felelőssége a beolvastatásuk? Nekem természetesebb lenne, hogy van egy betöltő objektumod, és az visszaad egy paramétereket tartalmazó objektumot. Vagy ez valami lazy loading akar lenni? (Mennyi a realitása, hogy lesz olyan request, aminél egyáltalán nem lesz szükség paraméterek kiolvasására?) A loadDataFromIni meg nyilván rossz név.
A ParametersFromFile nem túl szerencsés név (inkább függvénynév lehetne), de erről már volt szó.
A loader->parser->config betöltési lánc szerintem nem életszerű, a parsolás olyan dolog, ami kimondottan egy tárolási típushoz (fájl) kapcsolódik, DB-ben vagy PHP tömbben tárolt paramétereken nincs mit parsolni. Ha úgy tetszik, a parsolás a betöltés implementációs részlete. Úgyhogy én közvetlenül a loader objektumtól várnám a paraméterek visszaadását, és a parser lehetne egy segédobjektum, amit a fileloader példányosításkor megkap (vagy maga generál a fájlnévből - ezt lehetne dependency injection containerrel, de az általában bonyolultabb, minthogy megérje vesződni vele, egyszerűbb, ha kap egy parserfactory objektumot, és az tud fájlnévből parsert generálni). Vagy ha nem akarod túlbonyolítani, akkor egyszerűen a fileparser is lehet absztrakt, és azon belül inifileparser meg mindenféle egyéb.
Nevek...
Hogy a parsolás kimondottan fájlhoz tartozna? Ezzel vitatkoznék. Pl. láttam konkrétan olyan megvalósítást egy javas rendszerben, ahol egy táblában, alkalmazásonként tároltak konfigurációs adatokat, alkalmazásonként(alrendszerenként?) egy sor, soronként egy LOB, benne XMLben a szükséges konfig.
(hogy mi értelme így tárolni, nem tudom)
Ezt nem értem... Nem ezt csináltam?
Szándékaim szerint a loader konstruktora kapja paraméterként a parser objektumot és egy tömbben adja vissza a kulcs/érték párokat.
upd: létezik valami "szabvány" az elnevezésekre?
Mi a cél?
Az ApplicationSetup-ban nem túl jól néz ki, a loadDataFromIni elnevezésű függvény.
Szerintem hasznos lenne, ha megírnád az XML olvasót is, és akkor könnyebben látnánk, hogy melyik kódot kell kiemelni az olvasóba és melyik maradhat az ApplicationSetup-ban. Illetve, hogy az olvasás és elemzés miként válasszuk el. De mivel mindkét irány fájl olvasást használ, talán még érdekes lehet egy adatbázisból történő olvas is. Lehet, hogy akkor már egészen másféle absztrakt osztályokat kellene gyártani. Nekem kicsit az az érzésem, hogy az ini olvasást túláltalánosítottad, ám nem biztos, hogy valóban át lett gondolva, hogy pontosan milyen általánosításokra is lesz majd későbbiekben szükség.
Túláltalánosított
Az elsődleges cél nem igazán egy működőképes alkalmazás létrehozása volt, inkább valami olyan, hogy mennyire tudom használni az OOP-ről eddig "megtanult" dolgokat.
Eddig az jött le, hogy lehetőleg minél apróbb darabokból, minél általánosabb megvalósítással kellene dolgozni.
A reakciókból meg az, hogy ez nincs teljesen így. :-)
boilerplate kód - kezdek egyre jobban belekavarodni
Itt (vigyázz, ez egy PDF!) találtam egy rövid, érthetőnek tűnő magyarázatot.
És ez még jobban összezavart:
megsérti az információ elrejtés elvét, másrészt kiszolgáltatja az API mögötti implementációt,
hiszen egy teljes folyamat összes lépését ismerni kell, hogy használhassam.
Amikor DI-t használok, végeredményben megsértem az információ elrejtés elvét (ha jól értem), mert kívülről avatkozom az adott objektum működésébe.
Nem jól értem?
(gáz, de ez van: valami alapismeretek nagyon hiányoznak, de arról lekéstem, hogy iskolában kezdjem tanulni, használható anyagot meg mindeddig nem találtam a neten - gondolom, nem is fogok)
A Wikipedia szócikk korrektül
Az információrejtés pedig mindig egyensúlyozás. A függvényparaméterezés is rontja az információrejtést.
OK, egyensúlyozás. De
Hogy az általam megírt kód minősége milyen volt? Passz. Kollégák tudnák megmondani, de inkább a sebességemmel voltak gondok (lásd Karway topikja "nincs kedvem dolgozni" témában!), az elkészült programjaimra nem nagyon panaszkodtak.
ui: az a baj, hogy sokszor - ebben az esetben is - már a wikipedia cikkeinek hitelességében is kételkedem, mivel úgy tűnik, egyre kevésbé ellenőrzik őket. De ha szerinted nem írnak hülyeséget, akkor elfogadom.