Jump to: navigation, search

OTHowtos/Invoke Role Behavior

< OTHowtos
Revision as of 15:33, 8 March 2011 by Stephan.cs.tu-berlin.de (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Role behavior can be invoked in may different ways. Some of these ways are directly encouraged by the language, while others may be less obvious.

Roles encapsulated by their team

It is recommended to declare roles as protected by default. This has the effect that only the enclosing team and sibling roles can ever have a reference to the protected role. Thus the normal style to invoke role behavior is:

  • role methods are called by the enclosing team and/or by sibling roles.
public team class Family 
{
    protected class Child {
        void tickle() { scream(); }
    }
    protected class Father {
        void playWith(Child child) {
            child.tickle();
        }
    }
    Father theFather;
    Child[] children;
    public void doLeisureTimeStuff() {
        for (Child child : children)
            theFather.playWith(child);
    }
}

Roles intercept base method calls

Without the need to store any explicit reference to a role object, the role may actively intercept method calls of its bound base object using callin bindings (OTJLD §4). Thus:

  • role methods are invoked by callin bindings.
public team class Family 
 
    protected class Child playedBy Person {
        cry <- before becomeTired;
    }
    protected class Father playedBy Person {
        yawn <- after becomeTired;
    }
}
...
Person joe= ...;
joe.becomeTired(); // invokes yawning if joe is a father.

If role and base are developed together, a developer may already know which base method call will invoke which role method and thus may invoke a base method for the purpose of invoking the role method, too. In some situations the base class can even be used to give "hints" to the role about which events to intercept by adding empty methods to the base class. E.g., in the above example becomeTired might be an empty method. Instead of expecting sub-classes to override becomeTired with useful behavior, also callin bindings can be used for this purpose. Different roles may of course add different behavior to the same base method.

Transparently handling roles like base objects

The Transparent Role pattern shows how role objects can be freely passed around in the program. It does so by using an interface which both base and role implement. Holding a role in a variable of static type of this interface forgets about the role-ness, yet calling any methods via this transparent reference may invoke either a role method or a base method (indirectly via a callout binding - perhaps using inferred callout (OTJLD §3.1.j)).

public interface IPerson {
    void becomeTired();
    // other methods ...
}
public class Person implements IPerson { ... }
public team class Family 
{
    @SuppressWarnings("inferredcallout")
    protected class Father implements IPerson playedBy Person 
    {
        // one method from IPerson implemented here:
        public void becomeTired() { this.yawn(); }
        // other methods implicitly forwarded to the Person base object using inferred callouts
    }
    public IPerson getFather() { return theFather; }
}
IPerson joe= theSmiths.getFather();
joe.becomeTired();  // will yawn
joe.goToSleep(); // invoke Person behavior

Explicit public role behavior

So far, all styles of invoking role behavior did not expose any special methods outside the team. In other words, no implementation outside the team could exploit any knowledge about the capabilities of a role class. If to the contrary, code outside the team should be able to invoke methods that only exist in a role class, it may be desirable to deviate from the default protection of roles, and declare a role public instead. This lays the grounds for externalized roles (OTJLD §1.2.2).

Full role exposure

public team class Family 
{
    public class Father { /* methods like above */ }
    public Father getFather() { return theFather; }
}
final Family theSmiths= ...
Father<@theSmiths> joe= theSmiths.getFather();
joe.yawn();

If this style should be used in a base program that otherwise handles base objects only, a method is needed for retrieving a role object in order to invoke the role method,

  • Provide a method for explicitly lifting (OTJLD §2.3.2) a base object to a specific role:
public team class Family 
{
    public Father asFather(Person as Father pf) { return pf; }
}
Person joe= ...
Father<@theSmiths> joeAsFather= theSmiths.asFather(joe);
joeAsFather.yawn();

Team as facade for roles

If access to specific role methods should be granted, but generally roles should still be encapsulated, the team may provide selective access to role functionality, without exposing the role class itself. See the Team Component pattern for details.

public team class Family 
{
    protected class Father playedBy Person { ... }
 
    public void yawn(Person as Father pf) { // uses declared lifting
        pf.yawn();
    }
    public Person getFather() { return theFather; } // uses lowering
}
Person joe= theSmiths.getFather();
theSmiths.yawn(joe);

(sorry I must be tired if yawning is all I can think of ;-)