Vždycky se objekty dají napasovat na společné rozhraní - v nouzi použiješ proxy. Společné rozhraní však mívám už v okamžiku návrhu, teprve pak řeším implementaci komponent.
Ano, vždycky dají...... a často to bývá zbytečné žonglování, když se to dá vyřešit součtovým typem...
Zbytečné žonglování je to jen tehdy, když se snažíš udělat rozhraní až dodatečně místo předem.
Rád se poučím - jak v Javě implementuješ normální binární strom? V jazycích se součtovými typy se to dělá takhle:
data Tree a = Node (Tree a) (Tree a) | Leaf a
Jak to uděláš v Javě? Buď si uděláš dvě třídy (Node, Leaf) a budeš to přetypovávat. Nebo nad to uděláš společný interface (případně to rovnou vrazíš do jedné třídy), a budeš tam mít metody typu "isNode", "isLeaf", "getLeft", "getRight". To ti pro změnu nezabrání udělat "getLeft" na Leaf, takže ten společný interface je dost násilný. Nebo do interfacu dáš metodu "udělěj()" a budeš to v podstatě řešit přes inversion of control (což by byla zrovna tady docela lahůdka).
V Javě nedělám, ale v PHP tohle řeším právě přes to "udělěj()". Je to mnohem jednodušší než ostatní uvažovaná řešení. Hlavně tím odpadnou zbytečné metody "isNode", "isLeaf", "getLeft", "getRight" apod. Nehledě k tomu, že tento přístup by tě omezoval pouze na binární stromy.
Jo, když má člověk v arzenálu i součtový typ, i nějakou formu polymorfizmu, může si vybrat, a každé řešení má svoje pro a proti.
Když použiju součtový typ, těžko se mi rozšiřuje množina hodnot, nad kterými můžu operovat (musím editovat původní zdroják), ale snadno se mi rozšiřuje množina algoritmů, které s tím pracují (použiju pattern matching, mám zaručený exahustiveness checking).
Když použiju otevřený typ (interface/instance), snadno se mi rozšiřuje množina hodnot (prostě někde jinde udělám novou instanci a ono to s ní funguje), ale blbě se mi rozšiřuje množina algoritmů (chci třeba umět každou hodnotu vykreslit, což by znamenalo přidat do interface nějakou metodu "draw" - musím editovat původní zdroják).
Takže podle situace je prostě vhodnější jedno nebo druhé, součtový typ používám tam, kde očekávám že typ se nebude moc rozšiřovat (třeba AST nějakého daného jazyka), otevřený typ tam, kde se zas moc nebudou rozšiřovat operace nad tím typem, ale chci mít možnost přidat další hodnoty (třeba grafické widgety si chci mít možnost přidělat v nějakém rozšiřujícím modulu, ale operace přibývat nebudou - bude tam nějaké kreslení, události atp. a hotovo).
Když chci oboje, dostanu
https://en.wikipedia.org/wiki/Expression_problem :-)
Takže je to spíš rozhodnutí podle specifického případu, který řeším, než podle jazyka - ovšem za předpokladu, že mám slušný jazyk, který umí součtové typy
Jinak se to skutečně musí v OOP bez ADT řešit přes dispatch na nějaké "udělej". Ona to v praxi není zas taková tragédie, když si na to člověk zvykne, ale mít k dispozici skutečný součtový typ a pattern matching je samozřejmě mnohem lepší...