Forwarding

Origem: Wikipédia, a enciclopédia livre.

Na programação orientada a objetos, encaminhamento significa que o uso de um membro de um objeto (uma propriedade ou um método ) em um objeto diferente: seu uso é encaminhado para outro objeto. O encaminhamento é usado em vários padrões de design, onde alguns membros são encaminhados para outro objeto, enquanto outros são manipulados pelo objeto usado diretamente. O objeto de encaminhamento é freqüentemente chamado de objeto de recebimento e os membros de encaminhamento explícito são chamados de funções de envio .

Delegação[editar | editar código-fonte]

O encaminhamento costuma ser confundido com delegação formalmente, são conceitos complementares. Nos dois casos, existem dois objetos, e o primeiro objeto (o que recebeu, o receptor) usa o segundo objeto (o que foi recebido, o recebido), por exemplo, para chamar um método. Eles diferem no que self refere ao objeto receptor (formalmente, no ambiente de avaliação do método no objeto receptor): na delegação refere-se ao objeto remetente, enquanto no encaminhamento refere-se ao objeto receptor. Observe que self é frequentemente usado implicitamente como parte do envio dinâmico (resolução do método: a qual função um nome de método se refere).

A diferença entre encaminhamento e delegação é a ligação do parâmetro self no o recebido quando chamado pelo receptor. Com a delegação, o parâmetro self é vinculado ao receptor, enquanto o encaminhamento é vinculado ao o recebido. ... O encaminhamento é uma forma de reenvio automático de mensagens; delegação é uma forma de herança com ligação do pai (superclasse) no tempo de execução, em vez de no tempo de compilação / link como na herança 'normal'.

Por exemplo, dado o seguinte código:

// Sender
void n() {
 print("n1");
}

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

void n() {
 print("n2");
}

sob delegação, isso tera como saída m2, n1, porque n() é avaliado no contexto do objeto original, enquanto que no encaminhamento, isso produzirá m2, n2, porque n() é avaliado no contexto do objeto de recebimento.

No uso casual, o encaminhamento costuma ser chamado de "delegação" ou considerado uma forma de delegação, mas em uso cuidadoso, eles são claramente diferenciados pelo que self refere. Embora a delegação seja análoga à herança, permitindo a reutilização comportamental (e concretamente a reutilização de código ) sem alterar o contexto da avaliação, o encaminhamento é análogo à composição, pois a execução depende apenas do objeto de recebimento (membro), e não do objeto de envio (original). Nos dois casos, a reutilização é dinâmica, ou seja, determinada em tempo de execução (com base no objeto ao qual o uso é delegado ou encaminhado), em vez de estática, significando determinada em tempo de compilação / link (com base na classe da qual é herdada). Como herança, a delegação permite que o objeto de envio modifique o comportamento original, mas é suscetível a problemas análogos à frágil classe base ; enquanto o encaminhamento fornece um encapsulamento mais forte e evita esses problemas; veja composição sobre herança .  

Exemplos[editar | editar código-fonte]

Um exemplo simples de forwarding em Java: uma instância de B encaminha chamadas para o foo método de sua a campo:

class B {
  A a;
  T foo()
  { return a.foo(); }
}

Observe que, ao executar a.foo(), o objeto this (default) é a (um subtipo de A ), não o objeto original (uma instância de B ). Além disso, a precisa não ser uma instancia de A : pode ser uma instancia de um subtipo(através do casting). De fato, A nem precisa ser uma classe: pode ser uma interface / protocolo .

Contraste com herança, em que foo é definido em uma superclasse A (o qual deve ser uma classe, não uma interface), e quando chamado em um exemplo de uma subclasse B, que usa o código definido em A, mas o objeto this ainda é uma instância de B :

class A {
  T frente() { /* ... */ };
}
class B extends A {
}

Neste exemplo Python, classe B para a frente o frente método e a propriedade x para o objeto durante o campo a: usando estes em b (um exemplo de B ) é o mesmo como usá-los no b.a (o exemplo de A para o qual estes são encaminhado).

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.

Caso simples[editar | editar código-fonte]

Neste exemplo de Java, a classe Printer possui um método de impressão. Esse método de impressão, em vez de executar a impressão em si, encaminha para um objeto da classe RealPrinter. Para o mundo exterior, parece que o objeto Impressora está fazendo a impressão, mas o objeto RealPrinter é o que está realmente fazendo o trabalho.

classe tem uma

método. Esse método de impressão, em vez de executar a impressão em si, encaminha para um objeto da classe. Para o mundo exterior, parece que a objeto está fazendo a impressão, mas o objeto é o que realmente está fazendo o trabalho.

Encaminhamento é simplesmente passar um dever para alguém / outra coisa. Aqui está um exemplo simples:

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();
  }
}

Caso Complexo[editar | editar código-fonte]

O caso mais complexo é um Padrão Decorator que, usando interfaces, o encaminhamento pode ser mais flexível e seguro . "Flexibilidade" aqui significa que

não precisa se referir a ou de qualquer maneira, como a mudança de encaminhamento é abstraída de. Neste exemplo, classe pode encaminhar para qualquer classe que implemente uma interface. Classe possui um método para alternar para outro encaminhador. Incluindo as cláusulas aprimoram a segurança de tipo, porque cada classe deve implementar os métodos na interface. A principal desvantagem é mais código.

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()
	}
}

Aplicações[editar | editar código-fonte]

O encaminhamento é usado em muitos padrões de design. O encaminhamento é usado diretamente em vários padrões:

  • Padrão
  • Padrão: o objeto decorador adiciona seus próprios membros, encaminhando outros para o objeto decorado.
  • Padrão: o objeto proxy encaminha o uso do membro para o objeto real.

O encaminhamento pode ser usado em outros padrões, mas geralmente o uso é modificado; por exemplo, uma chamada de método em um objeto resulta em vários métodos diferentes sendo chamados em outro: