Vícenásobná dědičnost – proč ji nezkusit impementovat v Nette, když není v PHP?
- Martin
- Člen | 171
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)
- Martin
- Člen | 171
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)
- Martin
- Člen | 171
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.
- Ondřej Mirtes
- Člen | 1536
Dědičnost je strašně nadužívaná, ve většině případů se pro řešení daleko lépe hodí kompozice.
- paranoiq
- Člen | 392
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
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.
- David Grudl
- Nette Core | 8218
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
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
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
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.