next up previous
Next: Safely and Efficiently Up: Introduction Previous: Background

Issues Involved in Supporting Behavioral Evolution

The basic problem with supporting behavioral evolution in static class-based languages like C++ is that there does not exist direct language support for allowing multiple implementations of a method for an object. The strategy pattern illustrates that trying to support dynamic behavior in a static, class-based model results in the following:

Representing the relation between the receiver and strategy as aggregation (reference or association) requires explicit delegation to the strategy object, with the receiver object being passed as an argument. The strategy object then executes the appropriate behavior for the receiver object. Conceptually, the implementation of m given in the Strategy class should be executed upon the receiver of the original message, namely the Receiver class instance. This corresponds to the self-reference principle supported with the implicit delegation relation [14]. In a language such as C++ that does not support implicit delegation, the alternative technique of explicit delegation will usually require the strategy class to be made a friend of the receiver (a friend is a class that is given access to the private members of another class), thus violating class encapsulation.

Far more significant than the issues associated with explicit delegation are those that arise from using the inheritance relation to group the alternative behavior implementations. While the inheritance relation may be useful for defining alternative implementations of a method in subclasses (method overriding), it is not adequate for allowing the implementation of a method to vary for an object at run-time. The strategy pattern tries to get around this by combining aggregation and inheritance, requiring an abstract strategy superclass to represent the common interface to the concrete subclasses that define the actual algorithm variations. As there is nothing for the concrete subclasses to inherit, the strategy superclass serves no purpose other than to define the common interface of its subclasses, which is often a duplication of the interface of the receiver class. Relying on the inheritance relation to group the alternative method implementations for the strategy will always require this extraneous class and interface duplication.

An alternative to modeling behavioral evolution with static aggregation and inheritance is the use of dynamic inheritance, as is found in prototype-based languages such as Self [24]. Dynamic inheritance, or delegation, allows the behavior of an object to evolve dynamically by modifying its parent relation to other objects. If an object does not have an implementation for a message it receives, the message is implicitly forwarded to its parent who either executes an implementation of the message on the receiver object, or continues forwarding of the message along the parent chain. An object may dynamically change its parent; thus it may portray different behavior for the same message. While dynamic inheritance avoids some of the problems associated with using static aggregation and inheritance to model behavioral evolution, prototype-based languages present other problems:

Dynamic inheritance allows an object's behavior to change by varying its parent. However, makes it more difficult to statically check that all messages will be understood. An additional problem associated with prototype-based languages arises from the inability to distinguish between treating an object as simply an object as opposed to a prototype (or class). Dynamically changing a property of an object may impact its children, which may or may not be the desired result. Finally, there are the performance issues associated with dynamic inheritance, in that the parent chain must be searched at run-time to find the implementation of a message.



next up previous
Next: Safely and Efficiently Up: Introduction Previous: Background



Karl Lieberherr
Tue Jan 21 09:24:10 EST 1997