Jump to content

Forwarding (object-oriented programming): Difference between revisions

From Wikipedia, the free encyclopedia
Content deleted Content added
Simple: format code
m change source to syntaxhighlight
Line 10: Line 10:


For example, given the following code:
For example, given the following code:
<source lang="Java">
<syntaxhighlight lang="Java">
// Sender
// Sender
void n() {
void n() {
Line 24: Line 24:
print("n2");
print("n2");
}
}
</syntaxhighlight>
</source>
under delegation this will output <samp>m2, n1</samp> because <code>n()</code> is evaluated in the context of the original (sending) object, while under forwarding this will output <samp>m2, n2</samp> because <code>n()</code> is evaluated in the context of the receiving object.<ref name="buechiweck"/>
under delegation this will output <samp>m2, n1</samp> because <code>n()</code> is evaluated in the context of the original (sending) object, while under forwarding this will output <samp>m2, n2</samp> because <code>n()</code> is evaluated in the context of the receiving object.<ref name="buechiweck"/>


Line 32: Line 32:
A simple example of explicit forwarding in Java: an instance of <code>B</code> forwards calls to the <code>foo</code> method of its <code>a</code> field:
A simple example of explicit forwarding in Java: an instance of <code>B</code> forwards calls to the <code>foo</code> method of its <code>a</code> field:


<source lang="Java">
<syntaxhighlight lang="Java">
class B {
class B {
A a;
A a;
T foo() { return a.foo(); }
T foo() { return a.foo(); }
}
}
</syntaxhighlight>
</source>
Note that when executing <code>a.foo()</code>, the <code>this</code> object is <code>a</code> (a subtype of <code>A</code>), not the original object (an instance of <code>B</code>). Further, <code>a</code> need not be an instance of <code>A</code>: it may be an instance of a subtype. Indeed, <code>A</code> need not even be a class: it may be an interface/[[Protocol (object-oriented programming)|protocol]].
Note that when executing <code>a.foo()</code>, the <code>this</code> object is <code>a</code> (a subtype of <code>A</code>), not the original object (an instance of <code>B</code>). Further, <code>a</code> need not be an instance of <code>A</code>: it may be an instance of a subtype. Indeed, <code>A</code> need not even be a class: it may be an interface/[[Protocol (object-oriented programming)|protocol]].


Contrast with inheritance, in which <code>foo</code> is defined in a superclass <code>A</code> (which must be a class, not an interface), and when called on an instance of a subclass <code>B</code>, it uses the code defined in <code>A</code>, but the <code>this</code> object is still an instance of <code>B</code>:
Contrast with inheritance, in which <code>foo</code> is defined in a superclass <code>A</code> (which must be a class, not an interface), and when called on an instance of a subclass <code>B</code>, it uses the code defined in <code>A</code>, but the <code>this</code> object is still an instance of <code>B</code>:
<source lang="Java">
<syntaxhighlight lang="Java">
class A {
class A {
T foo() { /* ... */ };
T foo() { /* ... */ };
Line 48: Line 48:
class B extends A {
class B extends A {
}
}
</syntaxhighlight>
</source>


In this Python example, class <code>B</code> forwards the <code>foo</code> method and the <code>x</code> property to the object in its <code>a</code> field: using these on <code>b</code> (an instance of <code>B</code>) is the same as using them on <code>b.a</code> (the instance of <code>A</code> to which these are forwarded).
In this Python example, class <code>B</code> forwards the <code>foo</code> method and the <code>x</code> property to the object in its <code>a</code> field: using these on <code>b</code> (an instance of <code>B</code>) is the same as using them on <code>b.a</code> (the instance of <code>A</code> to which these are forwarded).


<source lang="python">
<syntaxhighlight lang="python">
class A(object):
class A(object):
def __init__(self, x) -> None:
def __init__(self, x) -> None:
Line 85: Line 85:
b.x = 17 # b.a.x now has value 17
b.x = 17 # b.a.x now has value 17
del b.x # Deletes b.a.x.
del b.x # Deletes b.a.x.
</syntaxhighlight>
</source>


=== Simple ===
=== Simple ===
Line 93: Line 93:
Forwarding is simply passing a duty off to someone/something else. Here is a simple example:
Forwarding is simply passing a duty off to someone/something else. Here is a simple example:


<source lang="java">
<syntaxhighlight lang="java">
class RealPrinter { // the "receiver"
class RealPrinter { // the "receiver"
void print() {
void print() {
Line 114: Line 114:
}
}
}
}
</syntaxhighlight>
</source>


=== Complex ===
=== Complex ===
Line 120: Line 120:
The more complex case is a [[Decorator pattern#Java|Decorator Pattern]] that by using [[interface (Java)|interfaces]], forwarding can be made more flexible and [[type safety|typesafe]]. "Flexibility" here means that {{Java|C}} need not refer to {{Java|A}} or {{Java|B}} in any way, as the switching of forwarding is abstracted from {{Java|C}}. In this example, class {{Java|C}} can forward to any class that implements an interface {{Java|I}}. Class {{Java|C}} has a method to switch to another forwarder. Including the {{Java|implements}} clauses improves [[type safety]], because each class must implement the methods in the interface. The main tradeoff is more code.
The more complex case is a [[Decorator pattern#Java|Decorator Pattern]] that by using [[interface (Java)|interfaces]], forwarding can be made more flexible and [[type safety|typesafe]]. "Flexibility" here means that {{Java|C}} need not refer to {{Java|A}} or {{Java|B}} in any way, as the switching of forwarding is abstracted from {{Java|C}}. In this example, class {{Java|C}} can forward to any class that implements an interface {{Java|I}}. Class {{Java|C}} has a method to switch to another forwarder. Including the {{Java|implements}} clauses improves [[type safety]], because each class must implement the methods in the interface. The main tradeoff is more code.


<source lang=Java>
<syntaxhighlight lang=Java>
interface I {
interface I {
void f();
void f();
Line 158: Line 158:
}
}
}
}
</syntaxhighlight>
</source>


==Applications==
==Applications==

Revision as of 10:07, 25 July 2020

In object-oriented programming, forwarding means that using a member of an object (either a property or a method) results in actually using the corresponding member of a different object: the use is forwarded to another object. Forwarding is used in a number of design patterns, where some members are forwarded to another object, while others are handled by the directly used object. The forwarding object is frequently called a wrapper object, and explicit forwarding members are called wrapper functions.

Delegation

Forwarding is often confused with delegation; formally, they are complementary concepts. In both cases, there are two objects, and the first (sending, wrapper) object uses the second (receiving, wrappee) object, for example to call a method. They differ in what self refers to on the receiving object (formally, in the evaluation environment of the method on the receiving object): in delegation it refers to the sending object, while in forwarding it refers to the receiving object. Note that self is often used implicitly as part of dynamic dispatch (method resolution: which function a method name refers to).

The difference between forwarding and delegation is the binding of the self parameter in the wrappee when called through the wrapper. With delegation, the self parameter is bound to the wrapper, with forwarding it is bound to the wrappee. ... Forwarding is a form of automatic message resending; delegation is a form of inheritance with binding of the parent (superclass) at run time, rather than at compile/link time as with 'normal' inheritance.[1]

For example, given the following code:

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

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

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

under delegation this will output m2, n1 because n() is evaluated in the context of the original (sending) object, while under forwarding this will output m2, n2 because n() is evaluated in the context of the receiving object.[1]

In casual use, forwarding is often referred to as "delegation", or considered a form of delegation, but in careful usage they are clearly distinguished by what self refers to. While delegation is analogous to inheritance, allowing behavioral reuse (and concretely code reuse) without changing evaluation context, forwarding is analogous to composition, as execution depends only on the receiving (member) object, not the (original) sending object. In both cases, reuse is dynamic, meaning determined at run time (based on the object to which use is delegated or forwarded), rather than static, meaning determined at compile/link time (based on the class which is inherited from). Like inheritance, delegation allows the sending object to modify the original behavior, but is susceptible to problems analogous to the fragile base class; while forwarding provides stronger encapsulation and avoids these problems; see composition over inheritance.[1]

Examples

A simple example of explicit forwarding in Java: an instance of B forwards calls to the foo method of its a field:

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

Note that when executing a.foo(), the this object is a (a subtype of A), not the original object (an instance of B). Further, a need not be an instance of A: it may be an instance of a subtype. Indeed, A need not even be a class: it may be an interface/protocol.

Contrast with inheritance, in which foo is defined in a superclass A (which must be a class, not an interface), and when called on an instance of a subclass B, it uses the code defined in A, but the this object is still an instance of B:

class A {
    T foo() { /* ... */ };
}

class B extends A {
}

In this Python example, class B forwards the foo method and the x property to the object in its a field: using these on b (an instance of B) is the same as using them on b.a (the instance of A to which these are forwarded).

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.

Simple

In this Java example, the Printer class has a print method. This print method, rather than performing the print itself, forwards to an object of class RealPrinter. To the outside world it appears that the Printer object is doing the print, but the RealPrinter object is the one actually doing the work.

Forwarding is simply passing a duty off to someone/something else. Here is a simple example:

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

Complex

The more complex case is a Decorator Pattern that by using interfaces, forwarding can be made more flexible and typesafe. "Flexibility" here means that C need not refer to A or B in any way, as the switching of forwarding is abstracted from C. In this example, class C can forward to any class that implements an interface I. Class C has a method to switch to another forwarder. Including the implements clauses improves type safety, because each class must implement the methods in the interface. The main tradeoff is more code.

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

Applications

Forwarding is used in many design patterns.[2] Forwarding is used directly in several patterns:

Forwarding may be used in other patterns, but often use is modified; for example, a method call on one object results in several different methods being called on another:

References

  1. ^ a b c Büchi, Martin; Weck, Wolfgang (2000). "Generic Wrappers" (PDF). ECOOP 2000 — Object-Oriented Programming. Lecture Notes in Computer Science. Vol. 1850. pp. 212–213. doi:10.1007/3-540-45102-1_10. ISBN 978-3-540-67660-7.
  2. ^ Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John (1995). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley. Bibcode:1995dper.book.....G. ISBN 978-0-201-63361-0.