Skip to main content

Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

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.

RolePlaying.png

Class-level Binding

At class level you simply write (OTJLD §2.1):

public class Employee playedBy Person { /* details omitted */ }
Idea.png
Try this example
If 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):
public team class Company { public class 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):

Person joe = new Person("Joe");
Employee joeProgrammer = new Employee(joe);
Idea.png
Try this example
You 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).

CalloutGetName.png

Several syntactic options exist for callout method bindings:

// separate declaration and binding:
abstract public String getName();
getName -> getName;
 
// short-hand declaration-and-binding:
String getName() -> String getName();

A callout method binding may apply the following adaptations:

  • The given base method may be made available using a different name, e.g.,
getRealName -> getName;
char[] getName(boolean useFirstName, boolean useTitle) -> String getName(boolean title, boolean firstName)
    with {  useFirstName -> firstName, useTitle -> title, result <- result.toCharArray() }

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

CallinGetPhoneNo.png

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).
  • Use signatures to discrimitate between overloaded methods.
  • Use parameter mappings (see callout above) to adjust signatures (OTJLD §4.4).
  • Use labels to refer to callin bindings (OTJLD §4.1.e) for
    • overriding an inherited callin binding
    • 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.

DelegationGetContactData.png

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.

Back to the top