Pokud chci, aby všechny objekty, které používali staré rozhraní, šly použít na místě, kde potřebuji nové rozhraní, tak ať už to vyřeším novým rozhraním, nebo updatem starého, do těch všech objektů prostě šáhnout musím.
Nové rozhraní se vytváří většinou proto, aby se nemuselo zasahovat do věcí, které už fungují.
Definice nového rozhraní oproti modifikaci starého sice řeší to, že nemusím upravovat jiné klienty, pořád ale musím upravovat všechny servery a navíc mám dvě rozhraní řešící stejnou věc (a jelikož obě používám ke stejné věci, většina objektů bude muset implementovat obě).
Ono jde o to, co tu změnu iniciovalo. Například to, že jeden jediný klient (objekt, který rozhraní používá) požaduje novou funkcionalitu, kterou servery (objekty, které rozhraní implementují) zkrze staré nemohou implemenetovat. V tom případě s novým rozhraním vzniká zároveň i emulátor, který se snaží "nějak" novou funkcionalitu emulovat zkrze staré rozhraní, nebo nová funkcionalita prostě není k dispozici vůbec (multitouch může zkrze onetouch rozhraní emulovat starou funkcionalitu - onetouch myš). Tady se vám mění jen jeden klient.
Jako příklad uvedu rozhraní, které chci obohatit o funkci toString(). Klient, který chce
objekty tisknout se jich bude ptát, zda existuje rozhraní s funkcí toString(). Pokud existuje,
provede ji, pokud ne, emuluje ji například tak, že to převede na řetězec
"Unknown object:"+typeid(obj).name()
Po změně rozhraní sice bude existovat kvantum objektů, které se nebudou umět vytisknout,
ale časem takových objektů bude ubývat, jak bude zbývat čas tuto funkcionalitu
dodefinovávat. Projekt to ale nezastaví, nikoho to neblokuje, změna tedy nemusí být tak
drahá.
Z výše uvedeného dále vyplývá i výhoda, že nové rozhraní není povinné ve všech projektech. Některé projekty třeba nepotřebují objekty tisknout a tak nové rozhraní ani nebou používat, mohou používat staré objekty bez nového rozhraní. S novým rozhraním totiž
mohu změnu realizovat další úrovní dědění:
class ObjektSeStarymRozhranim: public IStareRozhrani {...};
class INoveRozhrani {...};
class ObjektSNovymRozhranim: public ObjektSeStarymRozhranim, public INoveRozhrani {};
My odkojení C++ dokonce tohle řešíme šablonou
template<class Base>
class ImplementaceNovehoRozhrani: public Base, public INoveRozhrani {...};
Kde Base je pak nějaký objekt, který implementuje IStareRozhrani. Šablona může definovat všechny funkce nového rozhraní, dokonce může zajistit i výchozí implementaci. Speciality se pak řeší specializačí šablony
template<>
void ImplementaceNovehoRozhrano<ObjektSeStarymRozhranim>::novaFunkce() {...};
Nemáte ani pravdu v tom, že servery musí implementovat obě rozhraní, nebo že rozhraní jsou duplicitní. Nové rozhraní většinou implementuje funkcionalitu, kterou straré rozhraní neumí... takže není duplicitní. Server také nemusí nutně implementovat obě. Může také komplet zahodit staré rozhraní, pokud se neplánuje, že nebude fungovat se starými klienty. V tom je opět svoboda, kdy o tom, co bude server implementovat rozhoduje majitel objektu, který rozhraní implementuje a není závislý na ničem jiném.
Takže již se mnou souhlasíte, že je to řešení lepší, akorát někdy může být neúměrně drahé? To je dobrý posun. :-)
Ani náhodou. To jste špatně pochopil. Lepší řešení nemůže být neúměrné drahé. Neuměrně drahé řešení je v konečném důsledku špatné řešení. Možná jen, když zanedbáme ekonomiku celého návrhu, ... jenže to je krátkozraké. Programátor by se neměl utápět ve svých paradigmatech aniž by se ohlížel na ekonomickou stránku celého vývoje. To je pak špatný programátor.
No a ted se stačí zamyslet nad tím, o kolik aplikaci zdraží rozhraní myslivce s více psy oproti jednomu. IMHO marginálně
(pokud budu vždy pracovat např. s prvním psem) a proto je lepší od začátku navrhnout multipsa.
Akademická diskuze