Using protected roles and lifting/lowering translations it is possible to develop a team such that it is a self-contained world, implemented purly in terms of its own roles. Conversely, any base classes only refer to other base classes. Still both worlds can communicate by way of the implicit object mapping realized as lifting and lowering.}}

==Team Activation==

==Team Activation==

Revision as of 12:41, 8 July 2010

Role containment is a superior version of class containment in Java.

Just like role playing, role containment is a relation that
is declared between classes but essentially affects run time instances:

Contents

Role Visibility

So far, roles behave just like inner classes. Next the developer of a role class must decide whether a role should be public or protected (there are no other choices).

Protected roles

It should be the normal case that roles are protected. This gives a team full control over the role. Only the team and sibling roles will be able to communicate with a protected role. To be more precise, it is the team instance that owns a role and acts as a fassade to it. So, the following method call, even when it occurs in a method of Company, is illegal if Employee is protected:

The compiler will complain about an illegal attempt to "externalize" (OTJLD §1.2.2) a protected role. Specifically, if getTeamLeader() returns an object of type Employee, this method can only be called with
this as the receiver, ensuring that nobody outside the owning team instance will ever get a reference to the team's role instances.

Further reading: with just the rules above an Employee instance could still leak out off the team by upcasting the role to some non-role supertype, ultimately to Object. The new top-level superclass Team.Confined provides means to prohibit upcasting even to Object (see OTJLD §7.2).

Public roles

By declaring a role public a pre-condition is met that instance of this type can actually be seen outside the enclosing team instance. Howeverm the type rules for "externalized roles" (OTJLD §1.2.2) still distinguish, e.g., the types Employee<@microsoft> and Employee<@canonical>, and consider both types as incompatible (in this example, microsoft and canonical are assumed to be instances of Company).

The usage of "externalized roles" using type syntax like Employee<@canonical> is actually not recommended for the novice. If, despite all recommendations, a role shall still be accessed from the outside of the team, it might be a good idea to simply declare an interface outside the team, let the role implement the interface and access the role via its non-role interface:

With this design we clearly distinguish which properties of a role should be visible from the outside.

Further reading: a severe restriction of inner classes in Java is the direct coupling of logical nesting and physical file structure. This simply doesn't scale for many tens of inner classes and perhaps nesting at more than two levels. To overcome these issues, OT/J supports an alternative file structure using "team packages" and "role files" (OTJLD §1.2.5).

Data Flows

Given that roles are normally protected, i.e., confined within their enclosing team instance, the question arises how information can be exchanged between a team and the outside. This is when the playedBy relation comes back into focus.

Role-to-base

Given that a role and its base are two sides of the same coin, the compiler can easily help with a little translation:

Only one thing has changed wrt the previous example: getTeamLader() now declares to return a Person. Well, the actual returned value this.leader isn't exactly of type Person, and Employee actually is not a subtype of Person, but the compiler can easily extract the underlying Person from any Employee instance and return this instead of the role.

This translation from a role instance to its base instance is called lowering. It is applied implicitly in all program locations where a role instance is provided which does not conform to the required type of the expression, but where extracting the base instance actually yields a value of the required type.

Base-to-role

The opposite direction is a bit more challenging: assume you have a base instance of type Person and want to use this as the handle refering to a role. The team could have multiple role types for the same base type, like when we introduce a second role Client playedBy Person to our running example.

In order to help the compiler here, the developer can use the following kind of method signature:

Here the argument someone has a two-sided type: the client has to provide an instance of type Person, but inside the method we want to speak to the Employee role of the person, since method acceptRequest is only defined in Employee. The translation from a base (Person) to a matching role (Employee) is called lifting, which has the following properties:

lifting requires three inputs

a base instance (explicitly provided by the client)

a team instance (implicit in non-static team methods)

a role type (here declared after as)

if all three inputs are identical across multiple invocations, lifting will always find the same role instance.

if lifting finds no existing role matching all three inputs a new role will be created on-demand and internally cached for later use.

lifting does not interfere with garbage collection.

Translations in method bindings

The most convenient way to use lifting and lowering is in method bindings.

Here the callout binding interprets the return value from Person.getSpouse() as an EmployeeSpouse, performing the necessary lifting fully automatic behind the scenes.

This is a summary of how lifting/lowering are supported in method bindings:

kind

return

argument

callout

lifting

lowering

callin

lowering

lifting

Disjoint but connected worldsUsing protected roles and lifting/lowering translations it is possible to develop a team such that it is a self-contained world, implemented purly in terms of its own roles. Conversely, any base classes only refer to other base classes. Still both worlds can communicate by way of the implicit object mapping realized as lifting and lowering.

Team Activation

Given that method call interception (by means of callin) is a significant intervention into an
existing class, it is important to precisely control when a callin binding should have an effect.

Consider the Person joe being asked for his phone number. Answering the office phone number only makes sense when joe is actually in the office so he will here the office phone ringing. In Object Teams we use team activation to represent a given context in which the program should perform its behavior.

Team activation is a feature of team instances. So the easiest way to activate a context is

Callin-enablement can be further restricted by guard predicatesOTJLD §5.4. Guard predicates are particularly useful for the Object Registration pattern, which ensures that only existing roles will be considered for callin dispatch.