Dekorování vráceného objektu podle kontaineru rubrika: Programování: Jiné
Mějme dvě třídy se společným předkem:
interface Item String getName() Parent getParent() class SimpleItem implements Item String getName() {...} Parent getParent() {...} class ComplexItem implements Item String getName() {...} Parent getParent() {...} Complex getComplex() {...}
a jakýsi další objekt ve dvou verzích:
class ContainerA Item addItem(Item item) {} Item getItems() {} class ContainerB Item addItem(Item item) {} Item getItems() {}
použití asi takovéto:
a = new ContainerA() a.addItem(new SimpleItem()) a.addItem(new ComplexItem()) b = new ContainerB() b.addItem(new SimpleItem()) b.addItem(new ComplexItem()) a.getItems()[0].getName() b.getItems()[0].getName() b.getItems()[0].getOptions()
- chci umožnit vytvářet specielní implementace.
- chtěl bych, aby ContainerB vracel všechny Item, a aby měli všechny Item krom stávajících metod ještě metody getAttribs() a getOptions() ale ContainerA nikoliv.
- getAttribs() a getOptions() jsou metody více méně obecné, které mohou a nemusí být přeimplementovány.
a/ Když udělám wrapper, upíšu se, a navíc mi všechny Item budou mět stejný typ. Třeba ItemB, ale nikoliv ComplexItem, či ComplexItemB.
b/ Když nacpu funkce do společného předka, budou mi tam zavadzet.
c/ Když si vynutím u ContainerB vlastní verzi ItemB (mající požadované metody), budu muset všechny implementace přepsat.
d/ Když odlehčím Item na jako jen obálku, a logiku budu vkládat kompozicí, tak to zase bude děsně neintuitivní při používání.
e/ Taky mě napadlo tam ty dvě metody nacpat pomocí extension method. Jenže si nejsem jist, zda to bude fungovat jen pro tu instanci, a obecně se mi to moc nepozdává.
víc mě nenapadá
Jak byste to řešili? Díky za reakce.
Pokud maji jen prvky ContainerB mit nejake metody navic, nemel by vracet stejny typ jako ContainerA. Definice rozhrani pro ContainerB takhle nedava moc smysl.
Kdyz budu mit jeden interface Item a druhy ExtendedItem (ktery implementuje Item a pridava ty dve metody getAttribs a getOptions), da se pro to celkem jednoduse napsat konkretni wrapper, ktery implementuje ExtendedItem, ale v konstruktoru vezme Item. Podle potreby si jeho vytvareni muze vzit na starost ContainerB (a dal prijimat Item) a nebo bude rovnou prijimat ExtendedItem a dekorovat budou jeho uzivatele sami. Stari uzivatele ContainerB muzou porad pouzivat jen interface Item a nijak je to nerozhaze.
Kdyz to vezmu poporade:
a) Dekorator nedava smysl, protoze rozsirujes ten interface. Pokud by bylo dekoratoru vic (pro Item i ExtendedItem), odekorovani ExtendedItemu dekoratorem pro Item by ztratilo kus typove informace. V Jave by mohla pomoct genericita, ale genericky dekorator smrdi.
b) Ve spolecnem predku nemaji IMHO co delat, kdyz nejsou spolecne. Item by pro ne pak musel vracet nejaky neuzitecny default nebo hazet vyjimku. Ani jedno nezni zrovna jako nejlepsi objektovy navrh.
c) I pokud ItemB bude implementovat Item a wrapovani si vezme na starost ContainerB? Existujici kod napsany pro Item by to mel normalne ustat.
d) To zni v OOP docela neprakticky, je to vlastne prechod na anemic model. Pokud te logiky ale neni moc, nemusi to nutne vadit. Jen to bude asi nekonzistentni se zbytkem kodu a nekoho to muze pri cteni kodu prekvapit.
e) Extension nebo default metody jsou pouzitelne last resort reseni, ale maji stejny problem jako bod b). Pokud bude ContainerB porad vracet Item, musely by rozsirovat vsechny Itemy.
Ja bych volil c) s moji upravou. Neni to nejcistsi a asi bude potreba kontrola proti vicenasobnemu wrapovani, ale zacleneni do existujiciho kodu by melo byt bez problemu a neovlivni to nijak samotny Item.
Pro plný přístup na Devel.cz se prosím přihlaste:
Nebo se přihlaste jménem a heslem:
Komentáře