Forwarding (object-oriented programming)

Last updated

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.

Contents

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:

// Sendervoidn(){print("n1");}// Receivervoidm(){print("m2, ");n();}voidn(){print("n2");}

Under delegation, m() will output m2, n1 because n() is evaluated in the context of the original (sending) object, while under forwarding, it 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:

classB{Aa;Tfoo(){returna.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:

classA{Tfoo(){/* ... */};}classBextendsA{}

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

classA:def__init__(self,x)->None:self.x=xdeffoo(self):print(self.x)classB:def__init__(self,a)->None:self.a=adeffoo(self):self.a.foo()@propertydefx(self):returnself.a.x@x.setterdefx(self,x):self.a.x=x@x.deleterdefx(self):delself.a.xa=A(42)b=B(a)b.foo()# Prints '42'.b.x# Has value '42'b.x=17# b.a.x now has value 17delb.x# Deletes b.a.x.

Simple

UML class diagram that illustrates forwarding. UML forwarding printer.svg
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:

classRealPrinter{// the "receiver"voidprint(){System.out.println("Hello world!");}}classPrinter{// the "sender"RealPrinterp=newRealPrinter();// create the receivervoidprint(){p.print();// calls the receiver}}publicclassMain{publicstaticvoidmain(String[]arguments){// to the outside world it looks like Printer actually prints.Printerprinter=newPrinter();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.

interfaceI{voidf();voidg();}classAimplementsI{publicvoidf(){System.out.println("A: doing f()");}publicvoidg(){System.out.println("A: doing g()");}}classBimplementsI{publicvoidf(){System.out.println("B: doing f()");}publicvoidg(){System.out.println("B: doing g()");}}// changing the implementing object in run-time (normally done in compile time)classCimplementsI{Ii=null;// forwardingpublicC(Ii){setI(i);}publicvoidf(){i.f();}publicvoidg(){i.g();}// normal attributespublicvoidsetI(Ii){this.i=i;}}publicclassMain{publicstaticvoidmain(String[]arguments){Cc=newC(newA());c.f();// output: A: doing f()c.g();// output: A: doing g()c.setI(newB());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:

Related Research Articles

A visitor pattern is a software design pattern that separates the algorithm from the object structure. Because of this separation new operations can be added to existing object structures without modifying the structures. It is one way to follow the open/closed principle in object-oriented programming and software engineering.

In software engineering, the adapter pattern is a software design pattern that allows the interface of an existing class to be used as another interface. It is often used to make existing classes work with others without modifying their source code.

The bridge pattern is a design pattern used in software engineering that is meant to "decouple an abstraction from its implementation so that the two can vary independently", introduced by the Gang of Four. The bridge uses encapsulation, aggregation, and can use inheritance to separate responsibilities into different classes.

In computer programming, lazy initialization is the tactic of delaying the creation of an object, the calculation of a value, or some other expensive process until the first time it is needed. It is a kind of lazy evaluation that refers specifically to the instantiation of objects or other resources.

In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, dynamically, without affecting the behavior of other instances of the same class. The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern as well as to the Open-Closed Principle, by allowing the functionality of a class to be extended without being modified. Decorator use can be more efficient than subclassing, because an object's behavior can be augmented without defining an entirely new object.

In software design and engineering, the observer pattern is a software design pattern in which an object, named the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.

In object-oriented programming languages, a mixin is a class that contains methods for use by other classes without having to be the parent class of those other classes. How those other classes gain access to the mixin's methods depends on the language. Mixins are sometimes described as being "included" rather than "inherited".

In computing, type introspection is the ability of a program to examine the type or properties of an object at runtime. Some programming languages possess this capability.

In object-oriented programming, delegation refers to evaluating a member of one object in the context of another original object. Delegation can be done explicitly, by passing the responsibilities of the sending object to the receiving object, which can be done in any object-oriented language; or implicitly, by the member lookup rules of the language, which requires language support for the feature. Implicit delegation is the fundamental method for behavior reuse in prototype-based programming, corresponding to inheritance in class-based programming. The best-known languages that support delegation at the language level are Self, which incorporates the notion of delegation through its notion of mutable parent slots that are used upon method lookup on self calls, and JavaScript; see JavaScript delegation.

<span class="mw-page-title-main">Method overriding</span> Language feature in object-oriented programming

Method overriding, in object-oriented programming, is a language feature that allows a subclass or child class to provide a specific implementation of a method that is already provided by one of its superclasses or parent classes. In addition to providing data-driven algorithm-determined parameters across virtual network interfaces, it also allows for a specific type of polymorphism (subtyping). The implementation in the subclass overrides (replaces) the implementation in the superclass by providing a method that has same name, same parameters or signature, and same return type as the method in the parent class. The version of a method that is executed will be determined by the object that is used to invoke it. If an object of a parent class is used to invoke the method, then the version in the parent class will be executed, but if an object of the subclass is used to invoke the method, then the version in the child class will be executed. This helps in preventing problems associated with differential relay analytics which would otherwise rely on a framework in which method overriding might be obviated. Some languages allow a programmer to prevent a method from being overridden.

<span class="mw-page-title-main">Java syntax</span> Set of rules defining correctly structured program

The syntax of Java is the set of rules defining how a Java program is written and interpreted.

In mathematics and in computer programming, a variadic function is a function of indefinite arity, i.e., one which accepts a variable number of arguments. Support for variadic functions differs widely among programming languages.

In object-oriented programming, inheritance is the mechanism of basing an object or class upon another object or class, retaining similar implementation. Also defined as deriving new classes from existing ones such as super class or base class and then forming them into a hierarchy of classes. In most class-based object-oriented languages like C++, an object created through inheritance, a "child object", acquires all the properties and behaviors of the "parent object", with the exception of: constructors, destructors, overloaded operators and friend functions of the base class. Inheritance allows programmers to create classes that are built upon existing classes, to specify a new implementation while maintaining the same behaviors, to reuse code and to independently extend original software via public classes and interfaces. The relationships of objects or classes through inheritance give rise to a directed acyclic graph.

The curiously recurring template pattern (CRTP) is an idiom, originally in C++, in which a class X derives from a class template instantiation using X itself as a template argument. More generally it is known as F-bound polymorphism, and it is a form of F-bounded quantification.

<span class="mw-page-title-main">Python syntax and semantics</span> Set of rules defining correctly structured programs

The syntax of the Python programming language is the set of rules that defines how a Python program will be written and interpreted. The Python language has many similarities to Perl, C, and Java. However, there are some definite differences between the languages. It supports multiple programming paradigms, including structured, object-oriented programming, and functional programming, and boasts a dynamic type system and automatic memory management.

In software engineering, a fluent interface is an object-oriented API whose design relies extensively on method chaining. Its goal is to increase code legibility by creating a domain-specific language (DSL). The term was coined in 2005 by Eric Evans and Martin Fowler.

In object-oriented computer programming, a null object is an object with no referenced value or with defined neutral (null) behavior. The null object design pattern, which describes the uses of such objects and their behavior, was first published as "Void Value" and later in the Pattern Languages of Program Design book series as "Null Object".

In object-oriented programming, "immutable interface" is a pattern for designing an immutable object. The immutable interface pattern involves defining a type which does not provide any methods which mutate state. Objects which are referenced by that type are not seen to have any mutable state, and appear immutable.

Objective-C is a high-level general-purpose, object-oriented programming language that adds Smalltalk-style messaging to the C programming language. Originally developed by Brad Cox and Tom Love in the early 1980s, it was selected by NeXT for its NeXTSTEP operating system. Due to Apple macOS’s direct lineage from NeXTSTEP, Objective-C was the standard programming language used, supported, and promoted by Apple for developing macOS and iOS applications until the introduction of the Swift programming language in 2014.

Apache Commons Logging is a Java-based logging utility and a programming model for logging and for other toolkits. It provides APIs, log implementations, and wrapper implementations over some other tools.

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.