Vícenásobná dědičnost – proč ji nezkusit impementovat v Nette, když není v PHP?

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Martin
Člen | 171
+
0
-

Ahoj. Už se to na fóru několikrát probíralo a vím, že mnozí vícenásobnou dědičnost zavrhují ze známých důvodů, ale:
1. Byla-li by v Nette implementována, nikdo přece nebude nucen ji používat.
2. Extension-methods v podstatě tím směrem jdou, přitom vlastně o něco méně čistě z hlediska OOP. Přitom je pravděpodobně stačí jen malinko rozšířit.
3. Používání instance jedné třídy v jiné jako atributu (často navrhované řešení) naprosto opomíjí rozdíl protected a public vlastností.
4. Problém konfliktu lze řešit velmi jednoduše určením hierarchie (inspirace viz Microsoft).
5. I za krátkou dobu, co s Nette (a vlastně i PHP) dělám, jsem už párkrát potřeboval obecné řešení pro určité chování, přidanou funkcionalitu. Vždy jsem tuto funkcionalitu musel přidávat znovu, nebo to řešit opisováním atributů či dokonce voláním statických (de facto globálních) funkcí. A to i tam, kde šlo o dědění i z hlediska přirozeného kontextu, zdaleka ne jen kvůli úspoře kódu. To není zrovna OOP čistota.
Samozřejmě netuším, zda se vícenásobná dědičnost neobjeví třeba v PHP 6, ale kdo ví, zda a vůbec šestka někdy bude.

Editoval Martin (28. 4. 2011 9:27)

kaja47
Člen | 16
+
0
-

Všechno řeší Traity v PHP 5.4

Martin
Člen | 171
+
0
-

Abych pravdu řekl, to mi trochu uniklo, až teď jsem třeba objevil i článek Experiment NetteExtras\ExtensionObjects zde na fóru. Ale po letmém prostudování nejsem přesvědčen, že Traity to opravdu vše řeší. Mě jde o případ: Chci použít hotovou třídu. Třeba od jiného autora, nějak licencovanou, prostě na ní nebudu nic měnit. Zároveň potřebuji podědit funkcionalitu nějaké jiné třídy, která není s výše zmíněnou v nijakém rozporu, jde o zcela nesouvisející záležitost. Pokud nebyla alespoň jedna z těchto tříd předem navrhována jako Trait, mám smůlu. Nebo ne? Nemám teď moc času to studovat, oprav mě, pokud se mýlím. Zatím mám spíš pocit, že by musely takto být navrhovány obě. Ale je fakt, že v Tebou zmíněném článku je uvedena jedna drobnost – PHP nemá explicitní definici pro virtuální metody. K tomu ještě OOP je v php poměrně nová záležitost a mnoho PHP programátorů se s jeho principy dříve nesetkalo. Jsem asi jeden z mála, který na stará kolena „downgradoval“ na PHP odjinud. Takže uvolnit tak mocný nástroj, jakým je vícenásobná dědičnost, by mohlo vést k vytváření hrůzných mutantů. No, až mi trocha času zbyde a pokud to zvládnu, asi si tu dědičnost implementuju jen tak pro sebe, pak sem hodím kód. Napadá mne poměrně rychlé řešení, ovšem výkonově to bude zoufalé a implementačně dost čuňárna.

Editoval Martin (28. 4. 2011 10:14)

Nilp
Člen | 65
+
0
-

5. I za krátkou dobu, co s Nette (a vlastně i PHP) dělám…

No offense, ale v tomhle bude ten problém.

Martin
Člen | 171
+
0
-

Nilp: Souhlas. Ale OOP se zabývám přes 20 let, psal jsem na to i disertaci. I proto jsem se k PHP dostal až teď, kdy začíná dohánět ostatní jazyky. Takže to neberu z hlediska PHP či Nette, ale OOP obecně. Samozřejmě je-li většinový názor, že vícenásobné dědění není to pravé, nebudu to nijak vnucovat.

Nilp
Člen | 65
+
0
-

Existují nějaké jazykové OOP prostředky a nějaké idiomatické postupy jak řešit problémy (které jsou evidentně dostačující, vícenásobnou třídní dědičnost má z OO jazyků snad jen C++, ne?), proč vymýšlet podivnosti využívající __call?

Editoval Nilp (28. 4. 2011 12:28)

Ondřej Mirtes
Člen | 1536
+
0
-

Dědičnost je strašně nadužívaná, ve většině případů se pro řešení daleko lépe hodí kompozice.

PJK
Člen | 70
+
0
-

Ondřej Mirtes napsal(a):

Dědičnost je strašně nadužívaná, ve většině případů se pro řešení daleko lépe hodí kompozice.

Stejným směrem táhnou třeba vývojáři Doctrine. Máš nějaké konkrétní příklady?

kaja47
Člen | 16
+
0
-

Vícenásobná dědičnost je Kraken, kterého jsme nikdy neměly vypouštět na světlo světa. Její implementace stejně bude glorifikovaná kompozice + delegování.

paranoiq
Člen | 392
+
0
-

proč se vlastně o vícenásobnou dědičnost pokoušet?

jednoduchá třídní dědičnost je vyjádřením vztahu 'is a', kompozice je vyjádřením vztahu 'has a', rozhraní je vyjádřením vlastnosti 'can do' a traity jsou znovupoužitelnou implementací této vlastnosti

tyto tři principy myslím dostatečně popisují cokoliv co je třeba s třídami provádět. dle mého je vícenásobná dědičnost věc, která ve slušném jazyce nemá co dělat. představa, že objekt je najednou jak Kočkou, tak i Psem ve mě zanechává lehký pocit schizofrenie

bohužel třeba v C++ jsou rozhraní simulována pomocí běžných tříd, virtuálních metod a vícenásobné dědičnosti a to může navádět k tomu, že vícenásobná dědičnost je něco dobrého. — není! opravdová rozhraní a traity jsou mnohem lepším řešením sdílení funkčnosti mezi různými třídami

dodatek:

hlavní problém v pochopení účelu dědičností je už v jejím zavádějícím názvu. ten navádí k představě, že třída A něco zdědí od třídy B ve „smyslu Petr zdědil dům po babičce“. třídní dědičnost obvykle ale neslouží k transferu vlastností z jedné třídy na druhou. slouží k určení typu. to že jsou s tímto typem asociovány nějaké vlastnosti a metody je až věc druhá

je-li dáno, že proměnná je jednoho typu, pak je vícenásobná dědičnost v daném jazyce nesmysl. (u prototypové nebo jiné dědičnosti to samozřejmě může být jinak)

Editoval paranoiq (28. 4. 2011 19:42)

Ondřej Mirtes
Člen | 1536
+
0
-

Největší problém je, že při podědění od jedné třídy si zamezíš dědění od čehokoli jiného, takže je potřeba si to pořádně rozmyslet a zvážit.

Důležitý je také substituční princip Barbary Liskov, který říká, že potomek by měl umět vše, co předek + případně ještě něco navíc. Běžně stačí vnímat význam slova „extends“, tedy že rozšiřuji, nikoli omezuji funkčnost předka.

Dědit jen z toho důvodu, abych měl v $this pohodlně přístupnou hromadu metod, je špatně.

Vícenásobná dědičnost pak zavádí do aplikace další zmatky. Pro mě je dostatečným argumentem proti ní to, že ji Java neimplementuje, ergo nepotřebuje.

Neomezujícím a silnějším nástrojem proti opakování kódu je kompozice, kdy v jednom objektu využívám jiné objekty, injektované přes konstruktor nebo metody.

Martin
Člen | 171
+
0
-

OK, nechme to být. Je to fakt, že vícenásobnou dědičnost jsem používal až v praxi mnohem později než v době, kdy jsem se zabýval teorií OOP. Asi bych k většině z uvedeného našel rozumné protiargumenty, ale netřeba se tím dál zabývat.

Editoval Martin (28. 4. 2011 21:06)

PJK
Člen | 70
+
0
-

A pokud jde o extension methods: To je jen syntaktické pozlátko. Sice „rozšiřují“ daný objekt, ale nemají možnost měnit vnitřní stav jinak než přez rozhraní. (Viz C#).

Ovšem, až přijdou traity, to už tu většina z nás nebude :D

David Grudl
Nette Core | 8218
+
0
-

Vícenásobnou dědičnost „umí“ NObject přes extension methods. Prostě místo tříd se použijí interfaces (kterých může třída implementovat víc a funguje i instanceof) a těla metod se vloží přes extension method:

Nette\Object::extensionMethod('IPrintable::print', function($This) {
	....
 });

No a všechny třídy implementující dané rozhraní mají metodu print.

Martin
Člen | 171
+
0
-

Vím, občas používám. Mimochodem je to zajímavě implementované. Jako obvykle je Nette 1 až několik verzí před PHP. I když zrovna toto na javě a podobných jazycích považuji spíše za nešťastnou úlitbu kvůli absenci některých jiných objektových principů (ovšem opravdu mé znalosti o teorii OOP spadají přibližně někam k roku 1988). Jen, pokud jsem si všiml, z extension methods nelze volat protected metody původního objektu, nebo se mýlím? Ale nechme to být, pokud budu klasické vícenásobné dědění někdy opravdu potřebovat, implementuji si to sám a komunita o něco takového zjevně nestojí, jsou tu důležitější věci k řešení.

Patrik Votoček
Člen | 2221
+
0
-

Martin napsal(a):

… Jen, pokud jsem si všiml, z extension methods nelze volat protected metody původního objektu, nebo se mýlím? …

Jednoduše ne… Ale dá se to „hacknout“ pomocí ReflectionMethod::setAccessible() stejně jako property.

Martin
Člen | 171
+
0
-

Tohle mě docela dostalo. Je fakt, že i v Microsoftím C++ jsem v odůvodněném případě (redukce drobné chybičky MFC způsobující zbytečné narůstání alokované paměti aplikace) volal private metodu MFC pomocí hacku ukazatele (tak před osmi lety, ta chyba je dávno opravena, předpokládám, že dnes už by to ani hacknout nešlo). Ale hledat na to přímo funkci, to mě nenapadlo. No, mám se tady ještě hodně co učit.