OTJ Primer/Role Playing

Role Playing is a new relation between classes/objects introduced by OT/J. Adding a role to an object has the effect of extending/specializing the existing object. Role playing is much more flexible than standard inheritance between classes.

Class-level Binding

Try this exampleIf you want to try this example, two requisites must be met: a class Person must already exist, and the declaration of Employee must be nested within a team class (see also OTJ_Primer/Role_Containment):

publicteamclass Company {publicclass Employee playedBy Person {}}

The above declaration has the effect that each runtime instance of Employee will be associated to a corresponding instance of Person. The compiler statically guarantees that there will never be an Employee instance without an associated Person instance. E.g., the default constructor for a bound role like Employee requires a non-null Person argument so you can write (OTJLD §2.4.1):

Try this exampleYou may try the above snippet within a method of team class Company.
When doing so you'll actually see a compiler warning. Can you figure out what is the issue and how the snippet can still be improved? If the warning message by itself is not clear you can always seek advice by using the "Go to Language Definition" action within the OTDT.

The syntax of OT/J does not allow direct access to the base link from a role instance to its base instance, but such access is controlled using callout method bindings, see next.

Method-level Bindings

Callout

Using a callout method binding a role can declare that it accepts messages by a certain name and that it will forward these messages to the associated base instance. We say a role acquires a base method (OTJLD §3).

Callin

A role may also declare callin method bindings, which define message dispatch in the opposite direction compared to callout: a message sent to a base object can be intercepted by a callin binding, which redirects the call to the role instance (OTJLD §4).

A basic callin binding looks like this:

getOfficePhoneNo <-replace getPhoneNo;

Callin bindings can be fine-tuned like this:

Chose between before, after and replace bindings. The former two variants are purely additive - meaning the base method is still executed, whereas the latter variant corresponds to overriding (OTJLD §4.2).

declaring precedence among several callin bindings to the same base method (OTJLD §4.8).

True Delegation

Just by combining callout and callin method bindings the effect of true delegation is obtained, meaning that during a delegated call, self-calls are still dispatched to the original object, here: the role.

No new syntax other than callout and callin method bindings is required to achieve true delegation.

Comparing Role-Playing and Inheritance

The above implies that the playedBy relation is very similar to inheritance:

just like with inheritance, a role may acquire methods from its base

unlike inheritance, such acquisition must be declared individually and can adjust mismatches

just like with inheritance, a role may override methods from its base

unlike inheritance, such overriding must be declared individually and can adjust mismatches

just like with inheritance, acquisition and overriding can be combined for the template-method pattern.

However, a role-playing relationship has these two properties that are not supported by inheritance:

role-playing is dynamic in that roles can come and go at any point during the life-cycle of a base object

role-playing supports multiple independent specializations because multiple role instances can be attached to the same base instance.

Moving on

The explanation of role playing given so far raises two essential questions:

if multiple roles are attached to the same base instance, how is the "correct" role selected during callin interception?

how can callin-interception be fine-tuned to apply in specific situations only?

For answering these questions, the picture has to be expanded to include also teams.