Přeposílání (objektově orientované programování) - Forwarding (object-oriented programming)
v objektově orientované programování, spedice znamená, že použití člena skupiny objekt (buď a vlastnictví nebo a metoda ) má za následek skutečné použití odpovídajícího člena jiného objektu: použití je předáno do jiného objektu. Přeposílání se používá v řadě designové vzory, kde jsou někteří členové předáváni jinému objektu, zatímco jiní jsou zpracováváni přímo použitým objektem. Objekt pro předávání se často nazývá a obalový objekta jsou voláni členové explicitního přeposílání funkce obálky.
Delegace
Spedice je často zaměňována s delegace; formálně se jedná o doplňkové koncepty. V obou případech existují dva objekty a první (odesílající, obalový) objekt používá druhý (přijímající, obálkový) objekt, například k volání metody. Liší se v čem já
odkazuje na přijímající objekt (formálně v hodnotící prostředí metody na přijímajícím objektu): v delegaci odkazuje na odesílající objekt, zatímco při předávání odkazuje na přijímající objekt. Všimněte si, že já
je často implicitně používán jako součást dynamické odesílání (rozlišení metody: ke které funkci se název metody vztahuje).
Rozdíl mezi předáváním a delegováním je vazba vlastního parametru v wrappee při volání prostřednictvím modulu wrapper. Při delegování je vlastní parametr vázán na obálku, při předávání je vázán na obálku. ... Přeposílání je forma automatického opětovného odeslání zprávy; delegace je forma dědičnosti s vazbou rodiče (nadtřídy) za běhu, spíše než v době kompilace / odkazu jako u „normální“ dědičnosti.[1]
Například vzhledem k následujícímu kódu:
// Odesílatelprázdnota n() { tisk(„n1“);}// Přijímačprázdnota m() { tisk(„m2“); n();}prázdnota n() { tisk(„n2“);}
pod delegací to bude výstup m2, n1 protože n ()
je vyhodnocován v kontextu původního (odesílajícího) objektu, zatímco při přeposílání bude vypsán m2, n2 protože n ()
je vyhodnocován v kontextu přijímajícího objektu.[1]
Při běžném používání se předávání často označuje jako „delegování“ nebo se považuje za formu delegování, ale při pečlivém používání se jasně odlišují tím, co já
odkazuje na. Zatímco delegování je analogické k dědictví, což umožňuje opětovné použití chování (a konkrétně opětovné použití kódu ) bez měnící se kontext hodnocení, přeposílání je obdobou složení, protože provedení závisí pouze na přijímajícím (členském) objektu, nikoli na (původním) odesílajícím objektu. V obou případech je opětovné použití dynamické, což znamená, že je určeno za běhu (na základě objekt na které je použití delegováno nebo předáno), spíše než statické, což je význam určený v době kompilace / odkazu (na základě třída z kterého se dědí). Stejně jako dědičnost umožňuje delegování odesílajícímu objektu upravit původní chování, ale je náchylné k problémům analogickým s křehká základní třída; zatímco předávání poskytuje silnější zapouzdření a předchází těmto problémům; vidět složení nad dědičností.[1]
Příklady
Jednoduchý příklad explicitního předávání v Javě: instance B
přesměruje hovory na foo
způsob jeho A
pole:
třída B { A A; T foo() { vrátit se A.foo(); }}
Všimněte si, že při provádění a.foo ()
, tento
objekt je A
(podtyp A
), nikoli původní objekt (instance B
). Dále, A
nemusí být instancí A
: může to být instance podtypu. Vskutku, A
nemusí být ani třída: může to být rozhraní /protokol.
Kontrast s dědictvím, ve kterém foo
je definována v nadtřídě A
(což musí být třída, nikoli rozhraní) a při volání na instanci podtřídy B
, používá kód definovaný v A
, ale tento
objekt je stále instancí B
:
třída A { T foo() { /* ... */ };}třída B rozšiřuje A {}
V tomto příkladu Pythonu třída B
předává foo
metoda a X
vlastnost k objektu v jeho A
pole: jejich použití na b
(instance B
) je stejné jako jejich použití na b.a.
(instance A
kterým jsou předávány).
třída A: def __init__(já, X) -> Žádný: já.X = X def foo(já): tisk(já.X)třída B: def __init__(já, A) -> Žádný: já.A = A def foo(já): já.A.foo() @vlastnictví def X(já): vrátit se já.A.X @X.seřizovač def X(já, X): já.A.X = X @X.deleter def X(já): del já.A.XA = A(42)b = B(A)b.foo() # Tiskne '42'.b.X # Má hodnotu '42'b.X = 17 # b.a.x má nyní hodnotu 17del b.X # Odstraní b.a.x.
Jednoduchý
V tomhle Jáva příklad Tiskárna
třída má tisk
metoda. Tato metoda tisku, namísto samotného tisku, předává objekt třídy RealPrinter
. Vnějšímu světu se zdá, že Tiskárna
objekt provádí tisk, ale RealPrinter
objekt je ten, kdo skutečně dělá práci.
Přeposílání je prostě přenesení povinnosti na někoho / něco jiného. Zde je jednoduchý příklad:
třída RealPrinter { // příjemce" prázdnota tisk() { Systém.ven.tisk("Ahoj světe!"); }}třída Tiskárna { // odesílatel" RealPrinter p = Nový RealPrinter(); // vytvoření přijímače prázdnota tisk() { p.tisk(); // zavolá přijímač }} veřejnost třída Hlavní { veřejnost statický prázdnota hlavní(Tětiva[] argumenty) { // do vnějšího světa to vypadá, že tiskárna skutečně tiskne. Tiskárna tiskárna = Nový Tiskárna(); tiskárna.tisk(); }}
Komplex
Složitějším případem je a Dekorativní vzor že pomocí rozhraní, spedice může být flexibilnější a bezpečný. „Flexibilita“ zde znamená to C
nemusí odkazovat A
nebo B
jakýmkoli způsobem, protože přepínání přesměrování je abstrahováno od C
. V tomto příkladu třída C
může přeposlat jakékoli třídě, která implementuje rozhraní Já
. Třída C
má metodu přepnutí na jiného předávacího modulu. Včetně nářadí
klauzule se zlepšují bezpečnost typu, protože každá třída musí implementovat metody v rozhraní. Hlavní kompromis je více kódu.
rozhraní Já { prázdnota F(); prázdnota G();} třída A nářadí Já { veřejnost prázdnota F() { Systém.ven.tisk("A: dělá f ()"); } veřejnost prázdnota G() { Systém.ven.tisk("A: dělá g ()"); }} třída B nářadí Já { veřejnost prázdnota F() { Systém.ven.tisk("B: dělá f ()"); } veřejnost prázdnota G() { Systém.ven.tisk("B: dělá g ()"); }} // změna implementačního objektu za běhu (obvykle se provádí v době kompilace)třída C nářadí Já { Já i = nula; // přesměrování veřejnost C(Já i){ setI(i); } veřejnost prázdnota F() { i.F(); } veřejnost prázdnota G() { i.G(); } // normální atributy veřejnost prázdnota setI(Já i) { tento.i = i; }} veřejnost třída Hlavní { veřejnost statický prázdnota hlavní(Tětiva[] argumenty) { C C = Nový C(Nový A()); C.F(); // výstup: A: dělá f () C.G(); // výstup: A: dělá g () C.setI(Nový B()); C.F(); // výstup: B: dělá f () C.G(); // výstup: B: dělá g () }}
Aplikace
Přeposílání se používá v mnoha návrhových vzorech.[2] Přeposílání se používá přímo v několika vzorcích:
- Vzorec odpovědnosti
- Dekorativní vzor: dekorační objekt přidává své vlastní členy a přeposílá ostatní na zdobený objekt.
- Proxy vzor: proxy objekt přeposílá použití členů na skutečný objekt.
Přeposílání může být použito v jiných vzorech, ale často je použití upraveno; například volání metody na jeden objekt má za následek volání několika různých metod na jiný:
Reference
- ^ A b C Büchi, Martin; Weck, Wolfgang (2000). „Generické obaly“ (PDF). ECOOP 2000 - objektově orientované programování. Přednášky z informatiky. 1850. str.212–213. doi:10.1007/3-540-45102-1_10. ISBN 978-3-540-67660-7.
- ^ Gamma, Erichu; Kormidlo, Richarde; Johnson, Ralph; Vlissides, Johne (1995). Návrhové vzory: Prvky opakovaně použitelného objektově orientovaného softwaru. Addison-Wesley. Bibcode:1995dper.book ..... G. ISBN 978-0-201-63361-0.