OTPattern/TeamComponent

Contents

Intent

A team should fully encapsulate its roles and provide an API for clients.

Motivation

The benefits of encapsulation are well-known. Using protected roles (OTJLD §1.2.3) in OT/J, it is easy to completely hide roles from access by clients external to the enclosing team.

Difficulties might arise, though, when defining the public interface of the team, because now data flows need to be defined without mentioning any role types. On the other hand, clients may need to refer somehow to individual role instances, when invoking methods of the team component.

A conventional solution might use surrogate identifiers for the communication between the component and its clients. All interface methods would then be required to translate surrogates to roles and back to surrogates. Also, when using String identifiers as surrogates no type safety is given, i.e., a surrogate being passed into an interface method may refer to an object of the wrong type.

Structure

As mentioned all roles of the team are declared as protected to ensure encapsulation. Next, roles are bound to globally visible classes using playedBy.

Public methods of the team component are defined with signatures that exploit lifting (OTJLD §2.3) and lowering (OTJLD §2.2) as the desired translations.

publicclass BaseA {/* details omitted */}publicteamclass TeamComponent {protectedclass RoleA playedBy BaseA {/* details omitted */}/** First API method with a data flow out off the team. */public BaseA outputMethod(){
RoleA a= ... // find a suitable role instance for this requestreturn a;// return `a` as a BaseA (by means of lowering)}/** Second API method with a data flow into the team. */publicvoid inputMethod(BaseA as RoleA a){// perform operations with `a` as a RoleA (thanks to lifting)}}publicclass Client {void clientMethod(TeamComponent t1){
BaseA a1= t1.outputMethod();// retrieve an instance from the team// more operations here
t1.inputMethod(a1);// pass the stored reference back to the team}}

Participants

Team TeamComponent contains one or more roles classes RoleA etc, and protects its instances against external access. Through the playedBy declaration RoleA and BaseA form one conceptual entity. Of this conceptual entity the BaseA part is publically visible.

Collaborations

When the Client invokes an outputMethod() the TeamComponent may return a role instance, but the role RoleA is first lowered to BaseA before being passed to the Client. Lowering happens transparently in order to resolve the typing conflict between a provided RoleA instance and the required return type BaseA.

Conversely, when the Client invokes an inputMethod() the public signature requires an argument of type BaseA, which can easily be provided by the Client. The team, however, needs a RoleA for its operation. This translation, lifting, is explicitly declared using the as keyword (OTJLD §2.3.2). Inside the method the argument indeed has the type RoleA.

Example

Consider a component for managing orders, OrderSystem. Internally the component handles OrderItems and Customers. Outside the team, a general warehouse module provides a class StockItem and a class Person is globally available. To create an order, a team method order() is called, passing the Person placing the order and a number of StockItems to be ordered. Internally, the Person becomes a Customer role and the StockItem becomes an OrderItem role.

Availability of specific items of a pending order can be queried using the same base objects (Person, StockItem) into the team. The lifting translation retrieves the same Customer and StockItem roles that were used when creating the order, so any properties of these roles (like the number of ordered pieces) can easily be retrieved.

Consequences

In order to fully support lifting and lowering even for collections, arrays can be used because lifting and lowering transparently work for arrays, too (OTJLD §2.2.e, OTJLD §2.3.d). Automatic translation of generic collections is unfortunately not possible.

Known uses

Two student assignments were based on this pattern: OrderSystem (from which the above example is an extract) and SportCourseManagement.