Jump to: navigation, search

OTPattern/TransparentRole

Intent

A role should be used transparently instead of its base.

Motivation

Within its enclosing team each role instance can be regarded as a decorator for its base instance. This is possible because the team is able to automatically lift the base instance to its role, i.e., translation polymorphism hides the role/base distinction.

Outside the team additional effort is needed to achieve substitutability of roles and bases.

Structure

If role and base classes implement a common interface, clients may use roles and bases by this interface in a substitutable way.

public interface ICommonInterface {
   void publicMethod1();
   void publicMethod2();
}
public class MyBase implements ICommonInterface {
   public void publicMethod1() { /* base implementation 1 */ }
   public void publicMethod2() { /* base implementation 2 */ }
}
public team class DecoratorManager {
   protected class DecoratorRole implements ICommonInterface playedBy MyBase {
      public void publicMethod1() { /* role implementation 1 */ }
      publicMethod2 -> publicMethod2;
   }
   public ICommonInterface decorate(MyBase as DecoratorRole o) { // lifts argument to DecoratorRole
      return o; // DecoratorRole is subtype of ICommonInterface
   }
}
// client code:
ICommonInterface obj= new MyBase();
obj.publicMethod1(); // base behavior 1
obj.publicMethod2(); // base behavior 2
obj= new DecoratorManager().decorate(obj);
obj.publicMethod1(); // role behavior 1!
obj.publicMethod2(); // base behavior 2

Collaboration

Client code my define variables of type ICommonInterface without knowing whether the referenced object is a base or a role. A role may act as a decorator which

  • overrides selected behavior (here: publicMethod1())
  • forwards all other methods (here: publicMethod2())

A team DecoratorManager can be used to manage role instances, transparently wrapping existing base instances with roles (see method decorate(..), which applies declared lifting (OTJLD §2.3.2) to perform the work of decorating).

Implementation

If the interface ICommonInterface declares many methods and explicit callout bindings are considered an undue burden, inferred callouts OTJLD §3.1.j can be used instead. In order to do so, this feature has to be enabled by either

  • changing the compiler option for inferred callouts from Error to Warning
(also Ignore exists as an option, but Warning is recommended here), or
  • enabling the preference Java > Compiler > Errors/Warnings > Annotations >
[x] Suppress optional errors with '@SuppressWarnings'.

In either case any use of callout inference should be marked by a corresponding @SuppressWarnings declaration.

Thus, the above role could be re-written to

  @SuppressWarnings("inferredcallout")
  protected class DecoratorRole implements ICommonInterface playedBy MyBase {
      public void publicMethod1() { /* role implementation 1 */ }
  }

Here a callout binding for publicMethod2() will be inferred from the declared super-interface of DecoratorRole and a matching method in MyBase. Note that MyBase need not implement ICommonInterface for the inference to succeed, but using the interface inferred callouts are more robust, and substitutability at client side is based on this interface.

Consequences

This pattern makes roles globally visible, however, without using dependent types (OTJLD §9). This reduces encapsulation as compared to the normal way of defining roles as protected with no public accessibility, yet, the actual role class may remain invisible (protected).

Related Patterns

This pattern can be combined with the Connector pattern.