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.
OTPattern/TransparentRole
Contents
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
toWarning
- (also
Ignore
exists as an option, butWarning
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.