Dvojité odeslání - Double dispatch
Polymorfismus |
---|
Ad hoc polymorfismus |
Parametrický polymorfismus |
Podtypování |
v softwarové inženýrství, dvojité odeslání je speciální forma hromadné odeslání a mechanismus, který odesílá volání funkcí do různých konkrétních funkcí v závislosti na běhových typech dvou objektů zapojených do volání. Ve většině objektově orientovaný systémy, konkrétní funkce, která je volána z volání funkce v kódu, závisí na dynamickém typu jednoho objektu, a proto jsou známé jako jediné odeslání hovory, nebo jednoduše virtuální funkce hovory.
Dan Ingalls nejprve popsal, jak používat dvojí dispečink ve Windows Pokec, volat to mnohočetný polymorfismus.[1]
Přehled
Obecným problémem je řešení, jak odeslat zprávu různým metodám v závislosti nejen na příjemci, ale také na argumentech.
Za tímto účelem systémy jako CLOS nářadí hromadné odeslání. Dvojité odeslání je další řešení, které postupně snižuje polymorfismus v systémech, které nepodporují vícenásobné odeslání.
Případy užití
Dvojité odeslání je užitečné v situacích, kdy volba výpočtu závisí na typech modulu runtime jeho argumentů. Programátor může například použít dvojité odeslání v následujících situacích:
- Třídění smíšené sady objektů: algoritmy vyžadují, aby byl seznam objektů seřazen do kanonického pořadí. Rozhodnutí, zda jeden prvek přijde před jiným prvkem, vyžaduje znalost obou typů a případně nějakou podmnožinu polí.
- Adaptivní kolizní algoritmy obvykle vyžadují, aby kolize mezi různými objekty byly řešeny různými způsoby. Typickým příkladem je herní prostředí, kde se srážka mezi kosmickou lodí a asteroidem počítá odlišně od srážky mezi kosmickou lodí a vesmírnou lodí.[2]
- Algoritmy malby které vyžadují průsečíky překrývání skřítci být vykreslen jiným způsobem.
- Personální management systémy mohou odeslání různé typy pracovních míst pro různé pracovníky. A
plán
algoritmus, který je dán osobním objektem typovaným jako účetní a objekt úlohy typovaný jako inženýrství, odmítá plánování dané osoby pro danou úlohu. - Zpracování událostí systémy, které používají jak typ události, tak typ objektu receptoru k volání správné rutiny zpracování událostí.
- Zámek a klíč systémy, kde existuje mnoho typů zámků a mnoho typů klíčů a každý typ klíče otevírá více typů zámků. Nejen, že potřebujete znát typy zapojených objektů, ale podmnožina „informací o konkrétním klíči, které jsou relevantní pro zjištění, zda určitý klíč otevírá konkrétní zámek“, se mezi různými typy zámků liší.
Společný idiom
Společný idiom, jako v příkladech uvedených výše, je ten, že výběr příslušného algoritmu je založen na typech argumentů volání za běhu. Hovor proto podléhá všem obvyklým dodatečným nákladům na výkon, které jsou spojeny s dynamickým rozlišením hovorů, obvykle více než v jazyce podporujícím pouze odeslání jedné metody. v C ++ například volání dynamické funkce je obvykle vyřešeno a singl offset výpočet - což je možné, protože překladač zná umístění funkce v objektu tabulka metod a tak může staticky vypočítat posun. V jazyce podporujícím dvojnásobek Expedice, to je o něco nákladnější, protože kompilátor musí generovat kód pro výpočet posunu metody v tabulce metod za běhu, čímž se zvýší celková délka cesty instrukce (o částku, která pravděpodobně nebude vyšší než celkový počet volání funkce, což nemusí být příliš významné).
Příklad v Ruby
Běžným případem použití je zobrazení objektu na zobrazovacím portu, kterým může být obrazovka nebo tiskárna, nebo něco úplně jiného, co ještě neexistuje. Toto je naivní implementace toho, jak zacházet s těmito různými médii.
třída Obdélník def display_on(přístav) # vybere správný kód na základě třídy objektu případ přístav když DisplayPort # kód pro zobrazení na DisplayPort když PrinterPort # kód pro zobrazení na PrinterPort když RemotePort # kód pro zobrazení na RemotePortu konec koneckonec
Totéž by bylo třeba napsat pro Oval, Triangle a jakýkoli jiný objekt, který se chce zobrazit na médiu. Problém je v tom, že existuje více než jeden stupeň polymorfismu: jeden pro odeslání metody display_on do objektu a druhý pro výběr správného kódu (nebo metody) pro zobrazení.
Mnohem čistší a udržovatelnější řešení je pak provést druhé odeslání, tentokrát pro výběr správné metody pro zobrazení objektu na médiu:
třída Obdélník def display_on(přístav) # druhé odeslání přístav.display_rectangle(já) koneckonectřída Ovál def display_on(přístav) # druhé odeslání přístav.display_oval(já) koneckonectřída DisplayPort def display_rectangle(objekt) # kód pro zobrazení obdélníku na DisplayPort konec def display_oval(objekt) # kód pro zobrazení oválu na DisplayPort konec # ...konectřída PrinterPort def display_rectangle(objekt) # kód pro zobrazení obdélníku na PrinterPort konec def display_oval(objekt) # kód pro zobrazení oválu na PrinterPort konec # ...konec
Dvojité odeslání v C ++
Na první pohled se zdvojnásobení odeslání jeví jako přirozený výsledek přetížení funkce. Přetížení funkce umožňuje volané funkci záviset na typu argumentu. Přetížení funkce se však provádí v době kompilace pomocí "zmanipulované jméno "kde interní název funkce kóduje typ argumentu. Například funkce foo (int)
lze interně volat __foo_i a funkce foo (double)
lze volat __jídlo. Neexistuje tedy žádná kolize názvů a žádné vyhledávání virtuální tabulky. Naproti tomu dynamické odesílání je založeno na typu volajícího objektu, což znamená, že jej používá virtuální funkce (přepsání) místo přetížení funkce, a má za následek vyhledávání vtable. Zvažte následující příklad napsaný v C ++, kolizí ve hře:
třída Kosmická loď {};třída Kosmická loď Apollo : veřejnost Kosmická loď {};třída Asteroid {veřejnost: virtuální prázdnota Srazit se(Kosmická loď&) { std::cout << „Asteroid zasáhl vesmírnou loď n"; } virtuální prázdnota Srazit se(Kosmická loď Apollo&) { std::cout << „Asteroid zasáhl vesmírnou loď Apollo n"; }};třída Explodující asteroid : veřejnost Asteroid {veřejnost: prázdnota Srazit se(Kosmická loď&) přepsat { std::cout << „ExplodingAsteroid zasáhl vesmírnou loď n"; } prázdnota Srazit se(Kosmická loď Apollo&) přepsat { std::cout << „ExplodingAsteroid zasáhl vesmírnou loď Apollo n"; }};
Pokud máte:
Asteroid asteroid;Kosmická loď vesmírná loď;Kosmická loď Apollo kosmická loď Apollo;
pak z důvodu přetížení funkce,
asteroid.Srazit se(vesmírná loď); asteroid.Srazit se(kosmická loď Apollo);
vytiskne Asteroid zasáhl vesmírnou loď
a Asteroid zasáhl vesmírnou loď Apollo
, bez použití jakéhokoli dynamického odeslání. Dále:
Explodující asteroid explodující asteroid;explodující asteroid.Srazit se(vesmírná loď); explodující asteroid.Srazit se(kosmická loď Apollo);
vytiskne ExplodingAsteroid zasáhl vesmírnou loď
a ExplodingAsteroid zasáhl vesmírnou loď Apollo
respektive opět bez dynamického odesílání.
S odkazem na Asteroid
, používá se dynamické odesílání a tento kód:
Asteroid& theAsteroidReference = explodující asteroid;theAsteroidReference.Srazit se(vesmírná loď); theAsteroidReference.Srazit se(kosmická loď Apollo);
tiskne ExplodingAsteroid zasáhl vesmírnou loď
a ExplodingAsteroid zasáhl vesmírnou loď Apollo
, opět podle očekávání. Následující kód však nefunguje podle potřeby:
Kosmická loď& theSpaceShipReference = kosmická loď Apollo;asteroid.Srazit se(theSpaceShipReference);theAsteroidReference.Srazit se(theSpaceShipReference);
Požadovaným chováním je svázat tato volání s funkcí, která přebírá kosmická loď Apollo
jako argument, protože se jedná o instanční typ proměnné, což znamená, že by byl očekávaný výstup Asteroid zasáhl vesmírnou loď Apollo
a ExplodingAsteroid zasáhl vesmírnou loď Apollo
. Výstup je však ve skutečnosti Asteroid zasáhl vesmírnou loď
a ExplodingAsteroid zasáhl vesmírnou loď
. Problém je v tom, že zatímco jsou virtuální funkce odesílány dynamicky v C ++, přetížení funkcí se provádí staticky.
Výše popsaný problém lze vyřešit pomocí simulující dvojité odeslání, například pomocí a vzor návštěvníka. Předpokládejme, že stávající kód je rozšířen tak, aby oba Kosmická loď
a Kosmická loď Apollo
jsou uvedeny funkce
virtuální prázdnota Srazit se(Asteroid& v asteroidu) { v asteroidu.Srazit se(*tento);}
Potom, zatímco předchozí příklad stále nefunguje správně, přepracování hovorů tak, aby byla kosmická loď agentem, nám poskytne požadované chování:
Kosmická loď& theSpaceShipReference = kosmická loď Apollo;Asteroid& theAsteroidReference = explodující asteroid;theSpaceShipReference.Srazit se(asteroid);theSpaceShipReference.Srazit se(theAsteroidReference);
Vytiskne se Asteroid zasáhl vesmírnou loď Apollo
a ExplodingAsteroid zasáhl vesmírnou loď Apollo
, podle očekávání. Klíčem je to theSpaceShipReference.CollideWith (theAsteroidReference);
dělá za běhu následující:
theSpaceShipReference
je odkaz, takže C ++ vyhledá správnou metodu ve vtable. V tomto případě zavoláApolloSpacecraft :: CollideWith (asteroid &)
.- V rámci
ApolloSpacecraft :: CollideWith (asteroid &)
,v asteroidu
je odkaz, takžeinAsteroid.CollideWith (* toto)
bude mít za následek další vyhledávání vtable. V tomto případě,v asteroidu
je odkaz naExplodující asteroid
takExplodingAsteroid :: CollideWith (ApolloSpacecraft &)
bude volána.
Dvojité odeslání v C #
v C# při volání metody instance přijímající argument lze dosáhnout vícenásobného odeslání bez použití vzoru návštěvníka. Děje se to pomocí tradičního polymorfismu a zároveň vrháme argument na dynamický.[3] Pořadač za běhu zvolí přetížení příslušné metody v době běhu. Toto rozhodnutí vezme v úvahu typ běhu instance objektu (polymorfismus) i typ běhu argumentu.
Dvojitá expedice v Eiffelově síti
The Eiffelova programovací jazyk může přinést koncept agentů na problém dvojitého odeslání. Následující příklad použije konstrukci jazyka agenta na problém dvojitého odeslání.
Zvažte problémovou doménu s různými formami TVARU a kreslení POVRCHU, na které chcete nakreslit TVAR. SHAPE i SURFACE vědí o funkci zvané `draw 'samy o sobě, ale ne navzájem. Chceme, aby objekty těchto dvou typů vzájemně spolupůsobily ve dvojím odeslání pomocí vzoru návštěvníka.
Úkolem je získat polymorfní POVRCH, aby na sebe nakreslil polymorfní TVAR.
Výstup
Níže uvedený příklad výstupu ukazuje výsledky dvou objektů návštěvníka SURFACE, které jsou polymorfně předávány seznamem polymorfních objektů SHAPE. Vzor kódu návštěvníka zná pouze SHAPE a SURFACE obecně a nikoli o konkrétním typu ani jednoho. Místo toho se kód spoléhá na běhový polymorfismus a mechaniku agentů k dosažení vysoce flexibilního ko-variantního vztahu mezi těmito dvěma odloženými třídami a jejich potomky.
Nakresli Červené POLYGON na ETCHASKETCHdraw a Červené POLYGON na GRAFFITI_WALLdraw a Šedá OBDÉLNÍK na ETCHASKETCHdraw a Šedá RECTANGLE on GRAFFITI_WALLdraw a zelená QUADRILATERAL na ETCHASKETCHdraw a zelená QUADRILATERAL na GRAFFITI_WALLdraw a modrý PARALLELOGRAM na ETCHASKETCHdraw a modrý PARALLELOGRAM na GRAFFITI_WALLdraw a žlutá POLYGON na ETCHASKETCHdraw a žlutá POLYGON na GRAFFITI_WALLdraw a nachový OBDÉLNÍK na ETCHASKETCHdraw a nachový OBDÉLNÍK na GRAFFITI_WALL
Založit
Než se podíváme na SHAPE nebo SURFACE, musíme prozkoumat použití oddělené distribuce na vysoké úrovni.
Návštěvnický vzor
Vzor návštěvníka funguje tak, že objekt návštěvníka polymorfně navštíví prvky datové struktury (např. Seznam, strom atd.) A provede nějakou akci (volání nebo agenta) proti objektům polymorfních prvků v navštívené cílové struktuře.
V našem příkladu níže vytvoříme seznam polymorfních objektů SHAPE, navštívíme každý z nich s polymorfním POVRCHEM a požádáme, aby se SHAPE nakreslil na POVRCH.
1 udělat 2 - Tisk obrazců na povrchy. 3 místní 4 l_ tvary: ARRAYED_LIST [TVAR] 5 l_povrchy: ARRAYED_LIST [POVRCH] 6 dělat 7 vytvořit l_ tvary.udělat (6) 8 l_ tvary.rozšířit (vytvořit {POLYGON}.make_with_color ("Červené")) 9 l_ tvary.rozšířit (vytvořit {OBDÉLNÍK}.make_with_color ("Šedá"))10 l_ tvary.rozšířit (vytvořit {ČTYŘÚHELNÍK}.make_with_color ("zelená"))11 l_ tvary.rozšířit (vytvořit {ROVNOBĚŽNÍK}.make_with_color ("modrý"))12 l_ tvary.rozšířit (vytvořit {POLYGON}.make_with_color ("žlutá"))13 l_ tvary.rozšířit (vytvořit {OBDÉLNÍK}.make_with_color ("nachový"))14 15 vytvořit l_povrchy.udělat (2)16 l_povrchy.rozšířit (vytvořit {ETCHASKETCH}.udělat)17 l_povrchy.rozšířit (vytvořit {GRAFFITI_WALL}.udělat)18 19 přes l_ tvary tak jako ic_shapes smyčka20 přes l_povrchy tak jako ic_surfaces smyčka21 ic_surfaces.položka.drawing_agent (ic_shapes.položka.drawing_data_agent)22 konec23 konec24 konec
Začneme vytvořením kolekce objektů SHAPE a SURFACE. Poté provedeme iteraci nad jedním ze seznamů (SHAPE) a umožníme ostatním (SURFACE) postupně navštěvovat každý z nich. Ve výše uvedeném ukázkovém kódu navštěvují objekty SURFACE objekty SHAPE.
Tento kód provádí polymorfní volání na {SURFACE}. Kreslete nepřímo pomocí `drawing_agent ', což je první volání (odeslání) vzoru dvojitého odeslání. Předává nepřímého a polymorfního agenta (`drawing_data_agent '), což umožňuje našemu kódu návštěvníka vědět pouze o dvou věcech:
- Co je kreslící prostředek povrchu (např. Al_surface.drawing_agent na řádku # 21)?
- Co je agent výkresových dat tvaru (např. Al_shape.drawing_data_agent na řádku # 21)?
Protože SURFACE i SHAPE definují své vlastní agenty, náš návštěvní kód je osvobozen od nutnosti vědět, jaké je vhodné volání, polymorfně nebo jinak. Tato úroveň indirection a decoupling není jednoduše dosažitelná v jiných běžných jazycích, jako je C, C ++ a Java, s výjimkou buď prostřednictvím nějaké formy reflexe, nebo přetížení funkcí s párováním podpisů.
POVRCH
V rámci polymorfního volání na {SURFACE} .draw je volání agenta, které se stane druhým polymorfním voláním nebo odesláním ve vzoru dvojitého odeslání.
1 odložený třída 2 POVRCH 3 4 Vlastnosti {ŽÁDNÝ} - Inicializace 5 6 udělat 7 - Inicializovat proud. 8 dělat 9 drawing_agent := činidlo kreslit10 konec11 12 Vlastnosti -- Přístup13 14 drawing_agent: POSTUP [ŽÁDNÝ, TUPLE [TĚTIVA, TĚTIVA]]15 - Kreslící agent proudu.16 17 Vlastnosti {ŽÁDNÝ} -- Implementace18 19 kreslit (a_data_agent: FUNKCE [ŽÁDNÝ, TUPLE, TUPLE [název, barva: TĚTIVA]])20 - Nakreslete `a_shape 'na Current.21 místní22 l_výsledek: TUPLE [název, barva: TĚTIVA]23 dělat24 l_výsledek := a_data_agent (Neplatné)25 tisk ("Nakresli " + l_výsledek.barva + " " + l_výsledek.název + „zapnuto“ + typ + "% N")26 konec27 28 typ: TĚTIVA29 - Zadejte název aktuálního.30 odložený konec31 32 konec
Argument agent v řádku # 19 a volání v řádku # 24 jsou oba polymorfní a oddělené. Agent je odpojen, protože funkce {SURFACE} .draw nemá tušení, na jaké třídě `a_data_agent 'je založena. Neexistuje způsob, jak zjistit, z jaké třídy byl agent operace odvozen, takže nemusí pocházet ze SHAPE nebo od jednoho z jeho potomků. To je výrazná výhoda Eiffelových agentů oproti jediné dědičnosti, dynamické a polymorfní vazbě jiných jazyků.
Agent je dynamicky polymorfní za běhu, protože objekt je vytvořen v okamžiku, kdy je potřeba, dynamicky, kde je v té době určena verze objektivizované rutiny. Jedinou silně vázanou znalostí je typ výsledku - podpis agenta - tedy pojmenovaná TUPLE se dvěma prvky. Tento konkrétní požadavek je však založen na požadavku na uzavírací funkci (např. Řádek č. 25 používá pojmenované prvky TUPLE k naplnění funkce „draw“ POVRCHU), což je nezbytné a nebylo mu zabráněno (a možná ani nemůže být) .
Na závěr si povšimněte, jak se exportuje do libovolného klienta pouze funkce `drawing_agent '! To znamená, že kód vzoru návštěvníka (který je POUZE klientem této třídy) potřebuje pouze vědět o agentovi, aby mohl svou práci dokončit (např. Použít agenta jako funkci aplikovanou na navštívené objekty).
TVAR
Třída SHAPE má základ (např. Výkresová data) pro to, co je nakresleno, možná na POVRCHU, ale nemusí to být. Agenti opět poskytují nepřímá a třídní agnostika potřebná k tomu, aby co-variantní vztah s SHAPE byl co nejvíce oddělen.
Dále berte na vědomí skutečnost, že SHAPE poskytuje jako libovolný klient funkci „drawing_data_agent“ pouze jako plně exportovanou funkci. Jediný způsob interakce s programem SHAPE, kromě vytváření, je tedy pouze prostřednictvím nástroje `drawing_data_agent ', který používá JAKÝKOLI klient k nepřímému a polymorfnímu shromažďování dat výkresu pro SHAPE!
1 odložený třída 2 TVAR 3 4 Vlastnosti {ŽÁDNÝ} - Inicializace 5 6 make_with_color (a_color: jako barva) 7 - Make with `a_color 'as' color '. 8 dělat 9 barva := a_color10 drawing_data_agent := činidlo drawing_data11 zajistit12 color_set: barva.stejný_řetězec (a_color)13 konec14 15 Vlastnosti -- Přístup16 17 drawing_data_agent: FUNKCE [ŽÁDNÝ, TUPLE, jako drawing_data]18 - Datový agent pro kreslení.19 20 Vlastnosti {ŽÁDNÝ} -- Implementace21 22 drawing_data: TUPLE [název: jako název; barva: jako barva]23 - Data potřebná pro čerpání proudu.24 dělat25 Výsledek := [název, barva]26 konec27 28 název: TĚTIVA29 - Název objektu Aktuální.30 odložený konec31 32 barva: TĚTIVA33 - Barva proudu.34 35 konec
Příklad klasické kosmické lodi
Variace klasického příkladu kosmické lodi má jeden nebo více objektů kosmické lodi pohybujících se po vesmíru naplněných dalšími položkami, jako jsou darebáci asteroidy a vesmírné stanice. To, co chceme, je metoda dvojitého odeslání pro řešení narušení (např. Možných kolizí) mezi dvěma ko-variantními objekty v našem vesmírném vesmíru. V našem níže uvedeném příkladu bude výstupní exkurze našich USS Enterprise a USS Excelsior:
Hvězdná loď Enterprise mění pozici z A-001 na A-002. Hvězdná loď podniká vyhýbavé kroky, vyhýbá se asteroidu „Rogue 1“! Hvězdná loď Enterprise mění polohu z A-002 na A-003. Hvězdná loď podniká vyhýbavě a vyhýbá se asteroidu „Rogue 2“ '! Hvězdná loď Enterprise přenáší vědecký tým na Hvězdnou loď Excelsior, jakmile projdou! Hvězdná loď Enterprise mění pozici z A-003 na A-004. Hvězdná loď Excelsior mění polohu z A-003 na A-005. Hvězdná loď podniká vyhýbavé kroky, vyhýbá se asteroidům' Rogue 3 '! Starship Excelsior se nachází v blízkosti vesmírné stanice Deep Space 9 a je ukotvitelný. Hvězdná loď Enterprise mění pozici z A-004 na A-005. Hvězdná loď Enterprise přenáší vědecký tým na hvězdnou loď Excelsior, jakmile projdou! Prostor 9 a je dokovatelný.
Návštěvník
Návštěvník klasického příkladu kosmické lodi má také mechanismus dvojitého odeslání.
1 udělat 2 - Umožněte vesmírným objektům navštěvovat a pohybovat se ve vesmíru. 3 místní 4 l_universe: ARRAYED_LIST [SPACE_OBJECT] 5 l_podnikání, 6 l_excelsior: KOSMICKÁ LOĎ 7 dělat 8 vytvořit l_podnikání.make_with_name ("Podnik", „A-001“) 9 vytvořit l_excelsior.make_with_name („Excelsior“, „A-003“)10 vytvořit l_universe.udělat (0)11 l_universe.platnost (l_podnikání)12 l_universe.platnost (vytvořit {ASTEROID}.make_with_name („Rogue 1“, „A-002“))13 l_universe.platnost (vytvořit {ASTEROID}.make_with_name (Rogue 2, „A-003“))14 l_universe.platnost (l_excelsior)15 l_universe.platnost (vytvořit {ASTEROID}.make_with_name (Rogue 3, „A-004“))16 l_universe.platnost (vytvořit {VESMÍRNÁ STANICE}.make_with_name („Deep Space 9“, „A-005“))17 návštěva (l_podnikání, l_universe)18 l_podnikání.set_position („A-002“)19 návštěva (l_podnikání, l_universe)20 l_podnikání.set_position („A-003“)21 návštěva (l_podnikání, l_universe)22 l_podnikání.set_position („A-004“)23 l_excelsior.set_position („A-005“)24 návštěva (l_podnikání, l_universe)25 návštěva (l_excelsior, l_universe)26 l_podnikání.set_position („A-005“)27 návštěva (l_podnikání, l_universe)28 konec29 Vlastnosti {ŽÁDNÝ} -- Implementace30 návštěva (a_object: SPACE_OBJECT; a_universe: ARRAYED_LIST [SPACE_OBJECT])31 - `a_object 'navštíví` a_universe'.32 dělat33 přes a_universe tak jako ic_universe smyčka34 šek připojený {SPACE_OBJECT} ic_universe.položka tak jako al_universe_object pak35 a_object.meet_agent.volání ([al_universe_object.sensor_data_agent])36 konec37 konec38 konec
Dvojité odeslání lze vidět na řádku č. 35, kde dva nepřímí agenti spolupracují, aby poskytli dvě ko-variantní volání fungující v dokonalém polymorfním koncertu. Funkce „a_object“ funkce „visit“ má aplikaci „meet_agent“, která je volána s daty senzoru aplikace „sensor_data_agent“ pocházejícími z aplikace „al_universe_object“. Další zajímavou částí tohoto konkrétního příkladu je třída SPACE_OBJECT a její „setkání“ ' Vlastnosti:
Akce návštěvníka
Jedinými exportovanými funkcemi SPACE_OBJECT jsou agenti pro setkání a data senzorů, stejně jako schopnost nastavit novou pozici. Když jeden objekt (kosmická loď) navštíví každý objekt ve vesmíru, data senzoru se shromáždí a předají hostujícímu objektu v jeho agentu setkání. Tam se data senzoru ze senzoru_data_agent (tj. - položky datových prvků senzoru_data TUPLE, jak jsou vrácena dotazem sensor_data_agent), vyhodnotí proti aktuálnímu objektu a na základě tohoto vyhodnocení se provede postup (viz `setkání v SPACE_OBJECT níže). Všechna další data se exportují do {NONE}. Je to podobné jako u oborů C, C ++ a Java Private. Jako neexportované funkce jsou data a rutiny používány pouze interně každým SPACE_OBJECT. Nakonec si všimněte, že volání setkání s `tiskem 'nezahrnují konkrétní informace o možných následných třídách SPACE_OBJECT! Jedinou věcí nalezenou na této úrovni v dědictví jsou obecné relační aspekty založené zcela na tom, co lze znát z atributů a rutin obecného SPACE_OBJECT. Skutečnost, že výstup „tisku“ má pro nás, jako lidské bytosti, smysl na základě toho, co víme nebo si představujeme o hvězdných lodích, vesmírných stanicích a asteroidech, je pouze logické plánování nebo náhoda. SPACE_OBJECT není naprogramován s žádnými konkrétními znalostmi jeho potomků.
1 odložený třída 2 SPACE_OBJECT 3 Vlastnosti {ŽÁDNÝ} - Inicializace 4 make_with_name (jméno: jako název; pozice: jako pozice) 5 - Inicializovat aktuální pomocí `a_name 'a` a_position'. 6 dělat 7 název := jméno 8 pozice := pozice 9 sensor_data_agent := činidlo sensor_data10 meet_agent := činidlo setkání11 zajistit12 name_set: název.stejný_řetězec (jméno)13 position_set: pozice.stejný_řetězec (pozice)14 konec15 Vlastnosti -- Přístup16 meet_agent: POSTUP [ŽÁDNÝ, TUPLE]17 - Agent pro správu setkání s Currentem.18 sensor_data_agent: FUNKCE [ŽÁDNÝ, TUPLE, připojený jako sensor_data_anchor]19 - Agent pro vrácení dat senzoru proudu.20 Vlastnosti - Nastavení21 set_position (pozice: jako pozice)22 - Nastavte `pozici 'pomocí` a_position'.23 dělat24 tisk (typ + " " + název + "změní pozici z" + pozice + „do“ + pozice + ".% N")25 pozice := pozice26 zajistit27 position_set: pozice.stejný_řetězec (pozice)28 konec29 Vlastnosti {ŽÁDNÝ} -- Implementace30 setkání (a_sensor_agent: FUNKCE [ŽÁDNÝ, TUPLE, připojený jako sensor_data_anchor])31 - Detekovat a hlásit stav kolize Aktuální pomocí `a_radar_agent '.32 dělat33 a_sensor_agent.volání ([Neplatné])34 šek připojený {jako sensor_data_anchor} a_sensor_agent.last_result tak jako al_sensor_data pak35 -li ne název.stejný_řetězec (al_sensor_data.název) pak36 -li (pozice.stejný_řetězec (al_sensor_data.pozice)) pak37 -li ((al_sensor_data.is_dockable a is_dockable) a38 (is_manned a al_sensor_data.is_manned) a39 (is_manueverable a al_sensor_data.is_not_manueverable)) pak40 tisk (typ + " " + název + " je blízko " + al_sensor_data.typ + " " +41 al_sensor_data.název + "a je dokovatelný.% N")42 elseif ((is_dockable a al_sensor_data.is_dockable) a43 (is_manned a al_sensor_data.is_manned) a44 (is_manueverable a al_sensor_data.is_manueverable)) pak45 tisk (typ + " " + název + „přenáší vědecký tým na“ + al_sensor_data.typ + " " +46 al_sensor_data.název + "jak procházejí!% N")47 elseif (is_manned a al_sensor_data.není_obslužen) pak48 tisk (typ + " " + název + „vyhýbá se vyhýbání“ +49 al_sensor_data.typ + " `" + al_sensor_data.název + "'!% N")50 konec51 konec52 konec53 konec54 konec55 název: TĚTIVA56 - Název aktuálního.57 typ: TĚTIVA58 - Typ proudu.59 odložený60 konec61 pozice: TĚTIVA62 - Pozice proudu.63 is_dockable: BOOLEAN64 - Je Current dokovatelný jiným objektem s posádkou?65 odložený66 konec67 is_manned: BOOLEAN68 - Je Current objekt s posádkou?69 odložený70 konec71 is_manueverable: BOOLEAN72 - Je proud schopen se pohybovat?73 odložený74 konec75 sensor_data: připojený jako sensor_data_anchor76 - Data snímače proudu.77 dělat78 Výsledek := [název, typ, pozice, is_dockable, ne is_dockable, is_manned, ne is_manned, is_manueverable, ne is_manueverable]79 konec80 81 sensor_data_anchor: odnímatelný TUPLE [název, typ, pozice: TĚTIVA; is_dockable, is_not_dockable, is_manned, není_obslužen, is_manueverable, is_not_manueverable: BOOLEAN]82 - Kotva datového typu senzoru proudu.83 84 konec
Existují tři potomci tříd SPACE_OBJECT:
SPACE_OBJECTASTEROIDKOSMICKÁ LOĎVESMÍRNÁ STANICE
V našem příkladu se pro položky „Rogue“ používá třída ASTEROID, SPACESHIP pro dvě hvězdné lodě a SPACESTATION pro Deep Space Nine. V každé třídě je jedinou specializací nastavení funkce typu a určitých vlastností objektu. Název je zadán v rutině vytváření i v pozici. Například: Níže je uveden příklad SPACESHIP.
1 třída 2 KOSMICKÁ LOĎ 3 zdědit 4 SPACE_OBJECT 5 vytvořit 6 make_with_name 7 Vlastnosti {ŽÁDNÝ} -- Implementace 8 typ: TĚTIVA = "Hvězdná loď" 9 - 10 is_dockable: BOOLEAN = Skutečný11 - 12 is_manned: BOOLEAN = Skutečný13 - 14 is_manueverable: BOOLEAN = Skutečný15 - 16 konec
Jakýkoli RÁMEČEK v našem vesmíru je tedy dokovatelný, obsluhovatelný a obratný. Jiné objekty, jako jsou asteroidy, nejsou žádnou z těchto věcí. SPACESTATION, na druhé straně, je jak dokovací, tak s posádkou, ale není obratný. Když se tedy jeden objekt setká s druhým, nejprve zkontroluje, zda je pozice umisťuje do vzájemné blízkosti a zda jsou, pak objekty interagují na základě jejich základních vlastností. Všimněte si, že objekty se stejným typem a name jsou považovány za stejný objekt, takže interakce je logicky zakázána.
Příklad závěru Eiffelovy
Pokud jde o dvojité odeslání, Eiffel umožňuje designérovi a programátorovi dále odstranit úroveň přímých znalostí z objektu na objekt tím, že odděluje rutiny tříd od svých tříd tak, že z nich udělá agenty a poté je předá místo toho, aby vytvořili funkci přímého objektu hovory. Agenti mají také specifické podpisy a možné výsledky (v případě dotazů), což je činí ideálními statická kontrola typu vozidla, aniž by se vzdal konkrétních detailů objektu. Agenti jsou plně polymorfní, takže výsledný kód má pouze specifické znalosti potřebné k dokončení jeho místní úlohy. V opačném případě není přidána žádná zátěž údržby tím, že budou mít specifické znalosti o vlastnostech vnitřní třídy rozšířené kolem mnoha ko-variantních objektů. To zajišťuje použití a mechanika agentů. Jednou z možných nevýhod použití agentů je, že agent je výpočetně nákladnější než jeho protějšek pro přímé volání. S ohledem na to bychom nikdy neměli předpokládat použití agentů při dvojím odeslání a jejich použití ve vzorcích návštěvníků. Pokud lze jasně vidět návrhový limit, pokud jde o doménu typů tříd, které se budou podílet na ko-variantních interakcích, pak je přímé volání efektivnějším řešením, pokud jde o výpočetní náklady. Pokud se však očekává, že doména třídy zúčastněných typů poroste nebo se bude podstatně lišit, pak agenti představují vynikající řešení pro snížení zátěže údržby ve vzoru dvojitého odeslání.
Viz také
Reference
- ^ Jednoduchá technika pro manipulaci s více polymorfismem. Ve sborníku ze dne OOPSLA „86, Objektově orientované programovací systémy, jazyky a aplikace, strany 347–349, listopad 1986. Vytištěno jako SIGPLAN Notices, 21 (11). ISBN 0-89791-204-7
- ^ Efektivnější C ++, Scott Meyers (Addison-Wesley, 1996)
- ^ „Using Type dynamic (C # Programming Guide)“. Microsoft Developer Network. Microsoft. 30. září 2009. Citováno 25. května 2016.
... Rozlišení přetížení nastává v době běhu místo v době kompilace, pokud jeden nebo více argumentů ve volání metody má typ dynamický ...
tento článek potřebuje další citace pro ověření.Srpna 2008) (Zjistěte, jak a kdy odstranit tuto zprávu šablony) ( |