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.
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.
Contents
Class-level Binding
At class level you simply write (OTJLD §2.1):
public class Employee playedBy Person { /* details omitted */ }
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);
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).
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;
- Parameters may be re-mapped (OTJLD §3.2), e.g.:
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).
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.
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.