Az objektumorientált programozásban a továbbítás azt jelenti, hogy egy objektum egy tagjának (akár tulajdonságnak, akár egy metódusának) a használata azt eredményezi, hogy egy másik objektum megfelelő tagját használjuk valójában: egy másik objektumnak átadjuk a használatot. Az átirányítást számos tervezési mintában használják, ahol egyes tagokat egy másik objektumnak továbbítanak, míg másokat a közvetlenül használt objektum kezel. A továbbító objektumot gyakran csomagoló objektumnak nevezik, az explicit továbbító tagokat pedig csomagoló funkciónak.

Delegálás A továbbítást gyakran összekeverik a delegálással; formálisan ezek kiegészítő fogalmak. Mindkét esetben két objektum létezik, és az első (küldő, csomagoló) objektum a második (fogadó, csomagolandó) objektumot használja, például egy metódus meghívására. Különböznek abban, hogy mi utal önmagára a fogadó objektumra (formálisan a metódus kiértékelési környezetében a fogadó objektumra): átruházáskor a küldő objektumra, míg továbbításkor a fogadó objektumra utal. Vegyük figyelembe, hogy a "self" kulcsszó gyakran implicit módon van használva a dinamikus továbbítás részeként (metódus felbontása: melyik funkcióra utal a metódus neve).

Például, adott a következő kód: // Sender void n() { print("n1"); }

// Receiver void m() { print("m2, "); n(); }

void n() { print("n2"); } delegáláskor ez m2 és n1 kimenetet eredményez, mivel az n()értékét az eredeti (küldő) objektummal összefüggésben értékeli ki, míg a továbbítás alatt ez m2, n2 kimenetet eredményez mert n() a fogadó objektummal összefüggésben van kiértékelve.

Általános használat esetén a továbbítást gyakran "delegálásnak" nevezik, vagy átruházásnak tekintik, ám gondos használat esetén egyértelműen megkülönböztető, hogy a "self" mire utal. Míg a delegálás hasonlít az örökléshez, lehetővé téve a működésen alapuló újrafelhasználást (és konkrétan a kód újrafelhasználását) anélkül, hogy megváltoztatná a környezetét. A továbbítás hasonló a kompozícióhoz, mivel a végrehajtás csak a fogadó objektumtól függ, és nem az (eredeti) küldő objektumtól. Mindkét esetben az újrafelhasználás dinamikus, vagyis futási időben meghatározott (az objektum alapján, amelyre a felhasználást átruházzák vagy továbbítja), nem pedig statikus, azaz a fordítási időben meghatározott (az az osztály alapján, amelytől örökölt). Az örökléshez hasonlóan a delegálás lehetővé teszi a küldő objektum számára az eredeti viselkedés módosítását, de érzékeny a alaposztályhoz hasonló problémákra. A továbbítás erősebb beágyazást biztosít és elkerüli ezeket a problémákat, lásd az objektum-összetételt az öröklés felett.

Példák Az explicit továbbítás egyszerű példája Java-ban: a Bpéldány továbbítja a hívásokat a foo metódus a mezőjének:

class B { A a; T foo() { return a.foo(); } } Vegyük figyelembe, hogy az a.foo() végrehajtásakor a this objektum egy a (A altípusa), nem pedig az eredeti objektum (B példánya). Ezenkívül a -nak nem feltétlenül kell az A példányának lennie: lehet egy altípus példánya is. Valójában, A -nak nem is kell osztálynak lennie: lehet interfész.

Az örökléssel ellentétben, amelyben a fooértéket egy A szuperosztályban definiálják (amelynek osztálynak kell lennie, nem interfésznek), és amikorB, alosztály példányára hívják, akkor az A-ban definiált kódot használja, de ez az objektum még mindig B példánya: class A { T foo() { /* ... */ }; }

class B extends A { }

Ebben a Python-példában a B osztály továbbítja a foo metódustés az x property-t az a mezőben lévő objektumnak: ezek használata b-n (B példánya) ugyanaz, mint a b.a használata (A példánya, amelyhez ezek vannak továbbítva).

class A(object): def __init__(self, x) -> None: self.x = x

def foo(self): print(self.x)

class B(object): def __init__(self, a) -> None: self.a = a

def foo(self): self.a.foo()

@property def x(self): return self.a.x

@x.setter def x(self, x): self.a.x = x

@x.deleter def x(self): del self.a.x

a = A(42) b = B(a) b.foo() # Prints '42'. b.x # Has value '42' b.x = 17 # b.a.x now has value 17 del b.x # Deletes b.a.x.

Szimpla

Ebben a Java-példában a Printer osztálynak van print metódusa. Ez a print metódus ahelyett, hogy maga végrehajtaná a kiíratást, a RealPrinter osztályú objektumnak továbbítja ezt. A külvilág számára úgy tűnik, hogy a Printer objektum végzi a kiíratást, de a RealPrinter objektum az, amely valójában a munkát végzi.

Az átirányítás egyszerűen valamilyen kötelességet ruház át valaki másra. Íme egy egyszerű példa:

class RealPrinter { // the "receiver" void print() { System.out.println("Hello world!");

class Printer { // the "sender" RealPrinter p = new RealPrinter(); // create the receiver void print() { p.print(); // calls the receiver

public class Main { public static void main(String[] arguments) { // to the outside world it looks like Printer actually prints. Printer printer = new Printer(); printer.print();

Komplex

A bonyolultabb eset a díszítő minta, amely interfészek használatával rugalmasabbá és típusbiztonságosabbá teszi a továbbítást. A "rugalmasság" itt azt jelenti, hogy a C-nek semmilyen módon nem kell utalnia A-ra vagy B-re, mivel a továbbítás váltása C-ből származik. Ebben a példában a C osztály továbbíthat minden osztályhoz, amely implementálja az I interfészt. A C osztály rendelkezik egy metódussal, hogy tudjon váltani a továbbítók között. Az implements kulcsszó javítva a típusbiztonságot, mivel minden osztálynak implementálnia kell a metódusokat amelyeket az interfész tartalmaz. A fő kompromisszum az több kódot jelent.

interface I { void f(); void g();

class A implements I { public void f() { System.out.println("A: doing f()"); } public void g() { System.out.println("A: doing g()"); }

class B implements I { public void f() { System.out.println("B: doing f()"); } public void g() { System.out.println("B: doing g()"); }

// changing the implementing object in run-time (normally done in compile time) class C implements I { I i = null; // forwarding public C(I i){ setI(i); } public void f() { i.f(); } public void g() { i.g(); }

// normal attributes public void setI(I i) { this.i = i; }

public class Main { public static void main(String[] arguments) { C c = new C(new A()); c.f(); // output: A: doing f() c.g(); // output: A: doing g() c.setI(new B()); c.f(); // output: B: doing f() c.g(); // output: B: doing g()

Alkalmazások A továbbítást számos tervezési mintában használják. A továbbítást közvetlenül több mintában használják:

Felelősséglánc minta Díszítő/dekorátr minta: A díszítő objektum hozzáadja saját tagjait, majd továbbadja a díszített tárgyat. Helyettesítő/proxy minta

A továbbítás más mintákban is felhasználható, de a felhasználás gyakran módosul, például egy metódushívás egy objektumra több különféle metódus hívását eredményezi egy másikon:

Illesztő/Adapter minta Híd minta Homlokzat/Facade minta

Források

Fordítás

Kategória:Programozási paradigmák Kategória:Objektumorientált programozás