Adattípus automatikus konverziójának korlátozása
Sziasztok!
Mostanában ismerkedem a PHP OOP részével és itt bizony, ami korábban jó dolog volt (ti.: gyengén típusosság) most kezd egy kicsit aggályossá válni.
A kérdésem a következő: van-e valamilyen mód arra, hogy egy adott osztály tulajdonságának típusát úgymond "beleégessük" a kódba?
A példa:Magyarul azt szeretném elérni, hogy a konstruktorban megadott string típus maradjon érvényes bármilyen változó értékadásnál és pl. az 5.44-et ne konvertálja automatikusan lebegőpontossá, hanem maradjon string. Ki lehet ezt valahogy kényszeríteni anélkül, hogy minden metódus paraméterére a függvényen belül meghívok egy settype()-ot?
A másik kérdésem: a PHP5 elvileg támogatja kontruktorként a _construct függvényt, ugyanakkor, ha így helyezem el az osztályban, akkor nem hívódik meg normálisan. Van tippetek, hogy ez miért lehet?
Köszönöm.
■ Mostanában ismerkedem a PHP OOP részével és itt bizony, ami korábban jó dolog volt (ti.: gyengén típusosság) most kezd egy kicsit aggályossá válni.
A kérdésem a következő: van-e valamilyen mód arra, hogy egy adott osztály tulajdonságának típusát úgymond "beleégessük" a kódba?
A példa:
class foo {
// ide stringet várunk
private $bar;
public function foo() {
// típus meghatározása
$this->bar = (string) $this->bar;
// itt még string a típus
}
public function output($print) {
$this->bar = $print;
print gettype($this->bar).'<br />';
// output: "double"
print $this->bar;
}
}
$foo = new foo();
print $foo->output(5.44);
A másik kérdésem: a PHP5 elvileg támogatja kontruktorként a _construct függvényt, ugyanakkor, ha így helyezem el az osztályban, akkor nem hívódik meg normálisan. Van tippetek, hogy ez miért lehet?
Köszönöm.
Szerintem te megpróbálod
Ha egyszer stringet teszel bele, a következő alkalommal meg lebegőpontos értéket, akkor lebegőpontossá válik. Ez így működik.
Annyit tehetsz, hogy azokat a változókat, amiket fix típussal akarsz használni, nem értékadáson keresztül éred el, hanem getter/setter metódusok segítségével és ezek közül valamelyikkel kierőszakolod a megfelelő típust.
(de ha hülyeséget írtam, valaki majd kijavít)
––––––––––––––––––––––––––––––––
A konstruktort __construct-nak hívják, két _ jellel az elején.
Yepp, kell csinálni neki
Igen, kicsit megerőszakolás
Amit te és inf3rno is írtok, az gyakorlatilag ugyanaz, mintha egy metódusnál adott paraméterre meghívom a settype()-ot és így kényszerítem rá, hogy olyan típusú maradjon, amilyennek szánom.
Ezek szerint ennél tömörebben és egyszerűbben nem nagyon lehet ezt megoldani, igaz?
A __construct csalóka volt, köszi.
Hát amit a kódban írtál az
A másik, hogy az osztályok nevei mindig nagy betűvel kezdődnek, a harmadik meg, hogy mindegyik metódus public, mert csak úgy lehet unit test-et írni rá. A private-et egyébként sem érdemes használni, mert ha örököltetsz, akkor szórakozhatsz egy csomót az átírásával, jobb a protected helyette meg a final ha nem akarod, hogy örökíthető legyen az osztály. Szerintem jobban teszed, ha először alapvető dolgoknak utánaolvasol és csak utána állsz neki oo programozni.
A "mostanában ismerkedem..."
Nem értettem félre, amit írtál, csak épp nekem sokkal logikusabbnak tűnik, hogy adattípusonként írok egy metódust, ami értéket ad egy tulajdonságnak és egyúttal be is állítja a megfelelő (várt) adattípust. Ha tulajdonságonként kell írni egyet és van mondjuk húsz változód, akkor húsz metódus kell, így meg csak adattípusonként egy. Nem a lustaság miatt mondom, hogy kevesebb, csak épp nem értem, hogy miért kellene tulajdonságonként írni egyet, ha a típusonkénti meghívásukkal is ugyanazt azt eredményt érjük el (átkonvertáljuk a típust és értéket is kap). Miért jobb tulajdonságonként beállítani, mint adattípusonként?
A félreértések elkerülése végett: annyit szeretnék, hogy minden tulajdonság automatikusan a várt adattípusú legyen értékadáskor, így gyakorlatilag egy primív típusellenőrzés is történik. Ezt szerintem az én kódom is tudja.:) Hol van a gebasz?
A private-tel és a kisbetűs kezdéssel kapcsolatban jogos a felvetésed, tudjuk be mindezt a zöldfülűségemnek...:)
(Az előző user vagyok, csak közben megjött a jelszavam a mailcímemre.)
Kissé drasztikusan
Ha meg PHP-zni akarsz, akkor próbáld elfogadni, hogy ez egy gyengén típusos nyelv, a változónak mindig olyan a típusa, amilyen adatot beleteszel. És még örülj, ha a $v="5.67"; stringet ad és nem numerikus értéket! ;)
Ejha, nem gondoltam volna,
Lényeg a lényeg: ahogy említettem, most ismerkedem csak az OOP-alapú PHP-val, eddig csak "spagettikódokat" gyártottam, ami tudom, gyerekeknek való, de azokra a feladatokra, amikre nekem kellett, teljesen alkalmas volt.
Igazából csupán kíváncsiságból kérdeztem, hogy van-e erre mód, mert ugye a Java és a C sokkal szigorúbb nyelv ilyen szempontból és már létrehozáskor megköveteli, hogy megadjuk a változó típusát. Azt gondoltam (gondolom, javítsatok ki, ha tévedek), hogy ez a két nyelv - fogalmazzunk így - a programnyelvek hierarchiájában a PHP fölött van, azaz igazából azok a jó (jobb) módszerek, amik ezekben a nyelvekben vannak. Az érdekelt volna, hogy bár erőszak lenne, de van-e manuális módszer arra, hogy szigorúbbá tegyem a típusvizsgálatot a PHP-ban. Ennek célja igazából csak annyi lenne, hogy az osztályon kívülről érkező változók (pl. metódus paramétere) mindenképp "jó" típusúak legyenek és így a kód is nehezebben sebezhetővé, biztonságosabbá váljon.
Ha fogalmi vagy egyéb zavaraim lennének, akkor kérlek világosítsatok fel!:)
statikus- / dinamikus-, erős- / gyenge- típusosság
Szerintem nincsen ilyen hierarchia. Minden nyelvnek megvannak a szépségei és a hibái, és nincs ilyen szintű alá / fölé rendeltség. Valamelyik magasabb szintű nyelv mint a másik de ez csak a géphez való közelségét jellemzi, nem pedig hierarchiát. Ha típusos adatokat szeretnél kezelni, akkor használj olyan nyelvet, ami ez megköveteli, van belőle több tucat. A PHP nem típusos nyelv, és minden ilyen irányú kísérlet a nyelv megerőszakolása. Ezt meg kell szokni, vagy mást kell használni. A biztonságosságot pedig a bejövő adatok ellenőrzésével, konvertálásával és teszteléssel lehet elérni.
Az, hogy melyik a jobb módszer, szintén leginkább egyfajta vallási vita. Minden feladatra a megfelelő eszközt kell használni. Ha neked a Java / C statikus típusossága tetszik, vagy más megközelítése, van rengeteg nyelv, ami ezt tudja. De ha körülnézel, hogy statikus típusos, vagy dinamikus típusos nyelvekhez ért több fejlesztő, akkor láthatod, hogy valamiért a dinamikus típusos nyelvek nyernek. És ez nem csak a PHP érdeme. Ott van még a JavaScript, Python, a Perl, a Ruby, az Erlang stb.
Az dinamikus típusosság azt jelenti, hogy a változó akár milyen típusú lehet, sőt ez futás közben változhat is. A statikus típusosság azt jelenti, hogy a változónak előre meg van határozva a típusa, és attól nem térhet el.
Az erősen típusos nyelvekben a változók típusa meghatározza hogy egymással milyen műveletekre képesek. Azaz ha egy számot és egy stringet össze akarnánk adni erősen típusos nyelvben, akkor hibát kapunk. Ezzel szemben a gyengén típusos nyelvben automatikus típuskonverzió történik ebben az esetben, azaz a vagy a számot, vagy a stringet a másikba konvertálja a kód, majd elvégzi rajtuk a műveletet (az, hogy hogyan működik a típuskonverzió nyelvenként különböző). Egyébként gyengén típusos nyelvből elég kevés van, míg dinamikus típusos nyelvből pedig sok (a PHP mindkettő tulajdonsággal rendelkezik, akárcsak a JavaScript, bár a konvertálás szabályai különböznek).
Teszek egy kis kitérőt,
Ha kicsit belemászol még jobban az oop-be, találkozol azzal a logikával, hogy az objektumokon belüli változókat protected tulajdonsággal vesszük fel, és ezekre getter, setter metódusokat használunk.
Ennek a módszernek az az előnye, hogy ha az objektumon belül valamilyen drasztikus változtatást kell csinálni, vagy egy érték lekérdezéséhez-beállításához valamilyen logikát is hozzá kell fűzni, nem változik az objektum interface-e, csak az objektumban kell változtatást végrehajtanod, azon kívül marad minden a régiben. Írok erre egy példát:
van egy classod, ami egy webshop kosarat reprezentál, és a classodban vagy attributom (változó) ami a kosárban lévő termékek árának összességét tárolja.
És hogy visszakanyarodjunk az eredetileg feltett problémádhoz:
Tömbök és classok esetén használhatod a "type hinting" megoldást:
Igen, értem most már, valóban
Mea culpa.
Köszönöm.
Ott kezdődik a dolog, hogyha
A legjobb példa erre mondjuk egy 2d-s vektor. Megadhatod descartes koordinátákkal, de megadhatod polárkoordinátákkal is. A kettő átszámolható egymásba, bármikor lekérheted a koordinátákat bármelyik formában, de arról fogalmad sincs, hogy maga az osztály hogyan tárolja, mert kifele nem szabad, hogy ebből bármit is mutasson... Ugyanígy kívülről nem hívhatsz olyat, hogy setStr($property, $value). A másik ami miatt nem hívhatsz, mert amúgy sem túl jó gyakorlat, ha egy metódus nevében típus szerepel.
Miért lényeges neked, hogy
Az esetek 99%-ában teljesen mindegy.
(Talán) kezdem már kapisgálni
Egyébiránt elnézést a tudatlanságomért, de tudtommal abból tanul az ember, ha kérdez.:)
Nem jól gondolod. Az ilyen
Tehát akkor gyakorlatilag az
Most ez lehet, hogy hülye kérdés lesz, de ha már ennyire szétbontjuk a dolgokat, akkor egy objektum gyakorlatilag egy valamilyen módon összekapcsolódó (pl. célfeladat alapján) függvénycsoport, nemigaz?
Ha már itt tartunk: tudnátok ajánlani valamilyen jó könyvet, ami a szemléletmódot(!) magyarázza el, tehát nem konkrétan egy adott nyelv alapján íródott?
Pl. nekem az sem túl egyértelmű, hogy (ha már a 20 tulajdonságot is érdemes 4-5 osztályba szétszedni) miért jobb dolog az, hogy örököltetjük egyik osztályt a másikba, ahelyett, hogy fejlesztjük a szülőosztály funkcióit? Na, ez valóban egy teljesen más megközelítés, mint amivel eddig találkoztam...:)
Alapelvek röviden :)
Ezeket a nem túl egzakt fogalmakat azért valahogy be lehet sorolni kategóriákba.
A kohézió osztályozása (erősebbtől a gyengéébig): funkcionális (ugyanazt csinálja), szekvenciális (egymás után csinálják, gyártósor szerűen), kommunikációs (ugyanazon az adaton csinálják), procedurális (kb egymás után csinálják), temporális (időbeli kapcsolat van), logikai (valami logika miatt rakjuk őket egy modulba, tipikusan az Util fantázianevű csodák ilyenek) és a leggyengébb nyilván a véletlenszerű.
A csatolás (méretének) osztályozása (lazától az erősig): laza (primitív adatokkal kommunikál), stamp (valami összetett adat, pl objektum), control (valami vezérlést ad át), közös adat (nincs gazdája az adatnak), tartalmi csatolás (gyakorlatilag átírjuk a másik modul kódját).
Akkor persze szót kell arról ejtenünk, hogy mennyit kell tudnia egy modulnak. Azaz a fan-in/out értéke mennyi. A fan-out az általa meghívott modulok száma (ez 7 körül az optimális), fan-in pedig, hogy a modult hányan hívják. Ez utóbbira nincs igazán szabály, de fontos észben tartani.
És ez is csak a felelősség egy dimenziója... Megérne a téma egy hosszabb írást! :)
(Ja és itt még szó nem volt öröklésről, ami a tervezés gyakorlatilag utolsó mozzanata)
Tehát akkor gyakorlatilag az
Most ez lehet, hogy hülye kérdés lesz, de ha már ennyire szétbontjuk a dolgokat, akkor egy objektum gyakorlatilag egy valamilyen módon összekapcsolódó (pl. célfeladat alapján) függvénycsoport, nemigaz?
Ez igaz. Annyi extra van benne, hogy az adatokat, amiken ezek a függvények dolgoznak csak ők látják, mert ezek az adatok bele vannak zárva az objektumba. (Persze nyilván vannak be és kimenetek, ahol lehet állítgatni vagy lekérni őket, attól függően, hogy mire van szükség.)
Ilyen könyv nem létezik. Mindig kell példákat felhozni ha szemléletmódot magyarázol, és általában ezek a példák egy nyelvre szoktak korlátozódni. Én pl a clean code-ot ajánlom. Én is aszerint dolgozok egy ideje, és jobb úgy fejleszteni, kevesebbet hibázom.
Ez megintcsak újrahasznosítás. Képzeld el, hogy van két olyan osztályod, aminek vannak közös részei, de mégsem teljesen ugyanazok. Szerintem logikus, hogy ezeket a közös részeket kiemeled egy absztrakt osztályba, és onnan származtatod mindkettőt. pl: van kutya meg macska osztályod, mindkettő állat, tehát az állatra jellemző metódusaik azonosak, mondjuk mindkettő eszik meg tud mozogni... Fogod és az evés meg a mozgás metódusokat kiemeled egy absztrakt állatt osztályba. Így elkerülöd a kód duplázódást.
Típusosság - OOP
T.i. egy objektumon belül szerintem is egy tulajdonságnak egy típusának kell(ene) lennie, ugyanis, ha változik a típusa, akkor az már - szerintem - nem ugyanaz a tulajdonság.
Igaza van Poetronak abban, hogy ha nem tetszik, válassz(unk) másik nyelvet.De.
A PHP az egyik legelterjettebb szerver oldali nyelv.
A gyengén- ill. dinamikusan típusosságnak van egy hátránya: a sebesség. Minden változóeléréskor a php-nek meg kell állapítania az adat pillanatnyi típusát, ez alapján tud dolgozni az adattal. Ez pedig plusz köröket jelent, minden adateléréskor.
Bennem még mindig mocorog a kétely, hogy mennyire érdemes PHP-ben OOP-zni; ehelyett sok esetben inkább egy-egy fv.-t írok akármi.php-be, majd incude() v. require() azt jónapot. Lehet, hogy épp az OOP erőltetése a PHP megerőszakolása?
Nem biztos, hogy igazam van, ez csak egyéni véleményem.
T.i. egy objektumon belül
Már miért változna a típusa, úgy tárolod le, ahogy akarod, ha meg nagyon kell, hogy egy típus maradjon, akkor ki tudod kényszeríteni. Nekem ez eddig még sosem okozott problémát, mondjuk én nem is sűrűn foglalkoztam típusos nyelvekkel (kivétel java). Nekem az nem tetszik, hogy mondjuk egy számot string-re kell konvertálnom mielőtt hozzáfűzök egy másik stringhez. Plusz egy sor kód minden alkalommal...
Valaki írta még egy másik topicban, hogy php milyen hülye nyelv, hogy a string összefűzést "."-al csinálják és nem "+"-al, mint minden rendes nyelvben, de szerintem ez a megoldás jobb, mert egyértelműsíti, hogy stringekről van szó. Mondjuk a
Ez már inkább csak nézőpont kérdése
De ez tényleg nézőpont kérdése.
Szerk.:
Itt tényleg fontos, hogy legyen különbség a . és a + közt. Viszont egy típusos(abb) nyelvben lehet mindenütt +. De ez is csak nézőpont.
hát nem tudom, oop nélkül én
A gyengén tipusosság pont hogy tökéletes weboldalak készítéséhez, pl.: ott vannak a formok, input mező, számot vársz a felhasználótól, nem kell szórakozni azzal hogy konvertáld a stringet integerré, és nem látom, hogy ez mért lenne összeférhetetlen oop logikájával.
A form validálás már másik tészta, használjon az ember egy jó frameworkot és megcsinálja a dolog 80%-át, csak beállítani kell és az adott form csak megfelelő adatokat tartalmazhat.
Hát igen
Tesztelhetőség szempontjából
Re: Tesztelhetőség szempontjából
Fontos tesztelni, oké, az osztályok ebben segítenek, oké...
De konkrétan mit is csinálunk?
Van egy osztály, konstruktor függvényében átadunk mindenféle opciót, hogy annak megfelelően készítse el a HTML oldalt. Ezen mit lehet tesztelni? És ez majd miben segít nekünk a későbbiekben?
Van egy olyan irányelv, hogy
Ha több paraméter van, akkor többféleképpen lehet elkészíteni a HTML kimenetet, ha többféle kimenet van, akkor el lehet gondolkodni, hogy a közös részt kiemeled egy absztrakt osztályba, és minden kimenet típusnak csinálsz egy-egy osztályt, illetve készítesz egy provider-t, ami kimenet készítő osztályt csinál, a paraméterek alapján, stb...
Szóval itt arról van szó, hogy a kódod akkor átlátható, ha az osztály és metódus nevekből megmondod, hogy mit csinál. Ha van egy függvényed 5 paraméterrel, akkor abból nem biztos, hogy megmondod, hogy milyen esetben mit csinál...
A későbbiekben egyrészt abban segít, hogy hozzászoksz, az átlátható kód írásához meg ha módosítani akarod az aktuális oldalt, akkor nem remélhetőleg olyan kis részekre lesz darabolva, amik között már elég hamar megtalálod azt, amelyiket módosítani akarsz. Fel lehet ezt fogni úgy is, hogy amikor osztályokat és metódusokat csinálsz, akkor térképet csinálsz arról, hogy melyik része hol van a kódnak és mit csinál. Ha jól van megírva a kód, akkor az interface-ekből és az osztálynevekből ki lehet találni, hogy mit csinál.
Látom sikerült a lényeget
Úgy érted hogy lehet unit
Pl megadod neki a paramétereket, aztán megnézed, hogy a kimenet egyezik e azzal, amit vársz. Sima sztring összehasonlítás...
Ha elég kicsi részekre van feldarabolva a kimeneti HTML generálása, akkor egyáltalán nem bonyolult tesztelni. Ami még van unit test-nél az a selenium, az csinál egy dom fát a kimenetről, aztán lehet pl kattintani a gombokra vagy a linkekre benne, stb... Pontosan nem tudom ez milyen szinten van kidolgozva (pl van e javascript támogatottság benne), mert még nem használtam.
Úgy! :)
Az MVC minta esetén az modell esetén még csak-csak tudom, hogy mit kellene csinálni... de a nézet részt egyszerűen nem tudom. Sok munka és semmi haszna. (szó szoros értelmében semmi) De mivel mindenhol a TDD előnyeit hallom, ezért valószínűsítem valamit én csinálok rosszul, ezért kérdeztem rá, hogy egy HTML oldalt generáló egyszerű osztálynál mit csinálunk. Miért írtad azt, hogy "Tesztelhetőség szempontjából talán jobb, ha osztályok vannak."? Nulla számolás, nulla aritmetika, nulla logikai függőség, csupán string konkatenálás... mit vizsgálsz itt? És ez miben segít?
Ez ilyen meddő elméleti vita,
Meddő vita? :)
Ez nem meddő vita, mivel én még sehol sem láttam valódi TDD-t működés közben, ezért kaptam az alkalmat és a hozzászólásodra reagáltam. Remélve, hogy kapok konkrét példát.
Ahogy a TDD cikknél is leírtam, nem a TDD-t vitatom, hanem a működésre szeretnék példát kapni.
Belépés, jogosultság és a HTML megjelenítés gondolom nem ugyanannak az osztálynak a feladata, tehát a kérdés még adott, hogy a HTML megjelenítésre használt osztályt lehet-e egyáltalán úgy ellenőrizni, hogy az megfeleljen azon elvárásoknak, amiket a TDD-nél várunk.
Persze, hogy lehet, csak nem
Elméletileg lehet TDD-vel view réteget fejleszteni, ha már van egy kész HTML sablonod, aminél tudod, hogy melyik részekre mit akarsz betenni, és annak milyen a kimeneti HTML kódja... Azt hiszem a behúzások ebbe jó bele tudnak zavarni, gondolom ezt a sztring összehasonlításos problémát seleniummal át lehet valahogy hidalni, és DOM összehasonlítást csinálni, viszont mint már írtam én még nem használtam a seleniumot, szóval nem tudom mire képes... Ami biztos, hogy sztring összehasonlításos módszerrel fix, hogy nem csinálnám, mert macerás... Akkor inkább nem tesztelném a kinézeti részt...
Rajtunk kívül követi még valaki?
Amíg pl. az oldalhoz tartozik fixen 2, de legfeljebb 10 adminisztrátor (havi 1-2 hírt és képeket feltölteni), és rajtuk kívül csak a regelés nélküli látogatók, én bizony még mysql-t sem használok, hanem szép kis fájlocskákba mentem az adatokat meg azt a pár hírt. És összességében sokkal kevesebbet dolgoztam vele. Igaz, rázósabb, ha módosítani kell, de az ilyen nagyonegyszerűeket általában csak lecserélni kell (ha kell).
Ha van egy jó
Szerintem ejtsük
Zend fw elég használhatónak
framework használata mellett
egyszerű 8 lapból álló oldal, admin bejelentkezéssel symfony fw-el: beállítom sfDoctrineGuardPlugin, beállítom cms plugin, beállítom a dizájnt és megcsinálom a menüt (cms-ben van rá helper), db generálás autómatikusan, tesztelem, kész. Admin felületen ckeditorral szerkeszthető weboldalakat kapok (kép, fájl feltöltés minden nyalánkság). Mindez kb. 3 óra alatt és bármikor tovább fejleszthető.
Hogy most symfony, vagy zf, vagy akármi más, majdnem mindegy, ha lehet benne plugin-okat létrehozni, amiket újra lehet hasznosítani, az újrahasznosításon van a szó, egyszer szenvedsz vele sokat utána 100x felhasználod.
+1Tényleg csak ízlés
Tényleg csak ízlés kérdése, hogy melyik FW-t használod, bármelyikkel megéri foglalkozni...