Neljapäev, juuli 30, 2026

Ajalugu

TÄNA AJALOOS, 28. juuli ⟩ Hiinas toimus 20. sajandi suurim maavärin

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, wrapped) 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).

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:
    def __init__(self, x) -> None:
        self.x = x

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

class B:
    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

UML class diagram that illustrates forwarding.
UML class diagram that illustrates forwarding.

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

Lisa kommentaar

Sinu e-postiaadressi ei avaldata. Nõutavad väljad on tähistatud *-ga