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 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 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 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__(, X) -> Žádný:        .X = X    def foo():        tisk(.X)třída B:    def __init__(, A) -> Žádný:        .A = A    def foo():        .A.foo()    @vlastnictví    def X():        vrátit se .A.X    @X.seřizovač    def X(, X):        .A.X = X    @X.deleter    def X():        del .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řídatisk 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í . 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í  {	prázdnota F();	prázdnota G();} třída A nářadí  {	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í  {	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í  {	 i = nula;	// přesměrování	veřejnost C( 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( 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:

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

  1. ^ 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.
  2. ^ 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.