Twin - A Design Pattern For Modeling Multiple Inheritance: 1. Motivation
Twin - A Design Pattern For Modeling Multiple Inheritance: 1. Motivation
Inheritance
Hanspeter Mössenböck
1. Motivation
1.1 Example
GameItem Thread
draw() run()
bool intersects(otherItem) suspend()
for (all items x) collideWith(otherItem) resume()
if (intersects(x)) click() sleep()
collideWith(x); check()
The body of a ball thread is implemented in its run() method. When a ball thread is
running, it repeatedly moves and draws the ball. If the user clicks on a ball, the ball
sends itself a suspend() message to stop its movement. Clicking on the ball again
sends a resume() message to make the ball moving again.
The important thing about this example is that balls are both game items and
threads (i.e. they are compatible with both). They can be linked into a list of game
items, for example, so that they can be sent draw() and intersects() messages. But
they can also be linked into a list of threads from which the scheduler selects the next
thread to run. Thus, balls have to be compatible with both base classes. This is a
typical case where multiple inheritance is useful.
Languages like Java don't support multiple inheritance, so how can we implement
this design in Java? In Java, a class can extend only one base class but it can
implement several interfaces. Let's see, if we can get along with multiple interface
inheritance here. Ball could extend Thread and thus inherit the code of suspend() and
resume(). However, it is not possible to treat GameItem just as an interface because
GameItem is not fully abstract. It has a method check(), which contains code. Ball
would like to inherit this code from GameItem and should therefore extend it as well.
Thus Ball really has to extend two base classes.
This is the place where the Twin pattern comes in. The basic idea is as follows:
Instead of having a single class Ball that is derived from both GameItem and Thread,
we have two separate classes BallItem and BallThread, which are derived from
GameItem and Thread, respectively (Fig. 2). BallItem and BallThread are closely
coupled via fields so that we can view them as a Twin object having two ends: The
BallItem end is compatible with GameItem and can be linked into a list of game
items; the BallThread end is compatible with Thread and can be linked into a list of
threads.
GameItem Thread
draw() run()
… suspend()
click() resume()
check() sleep()
twin object
twin
BallItem BallThread
twin while (true) {
if (suspended) draw() run()
twin.draw();
twin.resume(); move()
twin.move();
else click()
twin.draw();
twin.suspend(); …
}
Fig. 2. The class Ball from Fig.1 was split into two classes, which make up a twin object
Twin objects are always created in pairs. When the scheduler activates a BallThread
object by calling its method run(), the object moves the ball by sending its twin the
messages move() and draw(). On the other hand, when the user clicks on a ball with
the mouse, the BallItem object reacts to the click and sends its twin the messages
suspend() and resume() as appropriate.
Using only single inheritance, we have obtained most of the benefits of multiple
inheritance: Active game objects inherit code from both GameItem and Thread. They
are also compatible with both, i.e. they can be treated both as game items (draw,
click) and as threads (run). As a pleasant side effect, we have avoided a major
problem of multiple inheritance, namely name clashes. If GameItem and Thread had
fields or methods with the same name, they would be inherited by BallItem and
BallThread independently. No name clash would occur. Similarly, if GameItem and
Thread had a common base class B, the fields and methods of B would be handed
down to BallItem and to BallThread separately—again without name clashes.
2. Applicability
3. Structure
Parent1 Parent2
v1 v2
M1() M2()
Child
v3
M1()
M2()
M3()
Parent1 Parent2
v1 v2
M1() M2()
twin
Child1 Child2
twin
v3 M2()
M1()
M3()
4. Participants
5. Collaborations
• Every child class is responsible for the protocol inherited from its parent. It handles
messages from this protocol and forwards other messages to its partner class.
• Clients of the twin pattern reference one of the twin objects directly (e.g. ballItem)
and the other via its twin field (e.g. ballItem.twin).
• Clients that rely on the protocols of Parent1 or Parent2 communicate with objects
of the respective child class (Child1 or Child2).
6. Consequences
Although the Twin pattern is able to simulate multiple inheritance, it is not identical
to it. There are several problems that one has to be aware of:
1. Subclassing the Twin pattern. If the twin pattern should again be subclassed, it is
often sufficient to subclass just one of the partners, for example Child1. In order to
pass the interface of both partner classes down to the subclass, it is convenient to
collect the methods of both partners in one class. One can add the methods of
Child2 also to Child1 and let them forward requests to the other partner (Fig.5).
Parent1 Parent2
v1 v2
M1() M2()
twin
Child1 Child2
twin
v3 M2()
M1()
M2() twin.M2()
M3()
Sub
M1()
M2()
This solution has the problem that Sub is only compatible with Child1 but not with
Child2. If one wants to make the subclass compatible with both Child1 and Child2
one has to model it according to the Twin pattern again (Fig.6).
Parent1 Parent2
v1 v2
M1() M2()
twin
Child1 Child2
twin
v3 M2()
M1()
M3()
gtwin
GrandChild1 GrandChild2
gtwin
M1() M2()
2. More than two parent classes. The Twin pattern can be extended to more than two
parent classes in a straightforward way. For every parent class there must be a
child class. All child classes have to be mutually linked via fields (Fig.7).
Parent1 Parent2 Parent2
v1 v2 v3
M1() M2() M3()
tw3
Child1 Child3
tw1
v4 M3()
tw2 tw2
M1() Child2
M4()
tw1 M2() tw3
7. Implementation
The following issues should be considered when implementing the Twin pattern:
1. Data abstraction. The partners of a twin class have to cooperate closely. They
probably have to access each others' private fields and methods. Most languages
provide features to do that, i.e. to let related classes see more about each other than
foreign classes. In Java, one can put the partner classes into a common package and
implement the private fields and methods with the package visibility attribute. In
Modula-3 and Oberon one can put the partner classes into the same module so that
they have unrestricted access to each others' components.
8. Sample Code
9. Known Uses
Applet StdMouseListener
resize() mouseEntered()
paint() mouseExited()
… mousePressed()
mouseClicked()
mouseReleased()
listener
MyApplet MyAppletListener
applet
paint() mousePressed()
mouseClicked()
mouseReleased()
Fig. 8. A twin applet that inherits code both from Applet and from StdMouseListener
MyApplet inherits code from Applet; MyAppletListener inherits code from StdMouse-
Listener. A MyAppletListener object will be registered as a mouse listener for
MyApplet. When it is notified about a mouse click it accesses its applet to perform an
appropriate action.
In [CaW98] a similar solution is presented using inner classes. MyAppletListener is
implemented there as an inner class of MyApplet. This allows MyAppletListener to
access all private instance variables of MyApplet. No explicit link between the classes
is necessary. However, this solution is asymmetric. MyApplet cannot access the
private instance variables of MyAppletListener.
The Twin pattern is related to the Adapter pattern, especially to the Two-Way-Adapter
described in [GHJV95], which is recommended when two different clients need to
view an object differently. However, the Two-Way-Adapter is implemented with
multiple inheritance while the Twin avoids this feature.
Acknowledgements
The technique described in this paper was discovered by Robert Griesemer in the
implementation of a game program in Oberon. It was also described—although not as
a design pattern—in [Tem93] and [Moe93].
References
[BMRSS96] Buschmann F., Meunier R., Rohnert H., Sommerlad P., Stal M.: Pattern-oriented
Software Architecture: A System of Patterns. Wiley 1996.
[CaW98] Campione M., Walrath K.: The Java Tutorial, 2nd edition, Addison-Wesley, 1998.
[GHJV95] Gamma E., Helm R., Johnson R., Vlissides J.: Design Patterns – Elements of
Reusable Object-Oriented Software. Addison-Wesley 1995.
[Moe93] Mössenböck H.: Objektorientierte Programmierung in Oberon-2. Springer-Verlag
1993.
[Pree95] Pree W.: Design Patterns for Object-Oriented Software Development. Addison-
Wesley 1995.
[Str89] Stroustrup B.: Multiple Inheritance for C++. Proceedings EUUG Spring Conference,
Helsinki, May 1989.
[Tem93] Templ J.: A Systematic Approach to Multiple Inheritance Implementation. SIGPLAN
Notices 28 (4): 61-66