Roles Before Objects

Author

Context

``Roles before objects'' is the first of a set of patterns for
organizing activities that separate object-independent from
object-dependent matters, in the interest of postponing or avoiding
object-level commitments during development and/or execution. It is
particularly well-suited to the development of open distributed
object systems in which object-independent descriptions take the form
of public interfaces. (See below for more guidance about when to use
this pattern.)

Problem

How do you encapsulate features, services and responsibilities
without knowing, caring about, committing to, revealing, or
representing the particular objects (or their classes) that will
possess or provide them?

Examples

Early in development, you might be able to encapsulate, say, a
PrintingService, without knowing or caring that it
will be supported by the same kinds of objects that provide a
CopyingService. These kinds of preliminary
decompositions often result from analysis of initial use-cases
(aka scenarios, stories) describing the externally visible
behavior of the system.

Especially in distributed applications, you need to allow for
the possibility that the objects that provide particular services may
change over time, and to ensure that this can happen ``transparently''.
Similarly, in fault-tolerant applications, you need to allow for
the possibility that a single service may be supported by multiple
redundant objects. Thus, you must structure designs and activities in
ways that avoid object-level descriptions.

Some ``architectural'' design patterns (including many of those
in the
Design Patterns book) and descriptions of idiomatic
constructions (as in our Object
Oriented System Development book) present high-level
fragments of capabilities that may be merged or combined with
others while coming up with a particular implementation. But
for many design purposes, you don't care about whether or how
this will be done.

Forces

Early in development, it is often easier and more productive to
deal with services, interfaces, and protocols without
committing to the existence of particular objects and classes
that support them.

If you do commit too early to the forms of objects (and their
classes), you can get stuck with suboptimal (or just plain bad)
designs, and will have to perform expensive and disruptive
rework. It is better to avoid this via scaffolding that allows
you to progressively commit to object-level computational
matters without backtracking, while at the same time allowing
for the implementation of default versions of some or
all features, in order to prototype and evaluate the design
without committing to its details.

The same kinds of issues can appear even during execution. If
messages are tied to particular objects, then you cannot, for
example, replace a faulty component with a fixed one without
doing a lot of rewiring. In some environments (notably CORBA), capabilities along these
lines are even mandated. Many aspects of CORBA systems and
services cannot be described at all at the level of classes and
objects.

Solution

The solution is to add a level of abstraction to object-based
constructs, the notion of a role. A role is an instance of
an encapsulated set of properties and behavior, but is described and
used in a way that is totally independent of the object(s) that may
implement it.

This definition is just a technical narrowing of the most common
everyday sense of the word role. For example, we can talk about the
role of Hamlet in a particular performance, without
reference to the actor(s) playing this role. The decision about who
plays this role is a separate ``casting'' decision. Normally we'd
like to pick the single actor providing the highest ``quality of
service''. But we would also like the freedom to swap in a designated
``second'' if the original actor gets sick, or perhaps even to use
two children, one on the other's shoulders and together dressed to
look like a single adult actor.

This notion of a role generalizes common OO techniques involving
abstract classes and related constructs, but elevates them to
first-class status. (Thus, for example, abstract classes are
special-case techniques in role-based development.) In particular,
there is a many-to-many relation between roles and objects. One role
may be implemented by either one object or by several otherwise
unrelated objects working together. Each may implement a subset of
operations, or redundantly implement all of them. For example, an
OfficeManager role might be implemented using:

a Person object, or

a Robot object, or

a set of job-sharing Persons, or

an inetd-style mechanism causing a new SpecialtyWorker object
to be constructed to handle each incoming service request, or

a collection of free-floating procedures activated in any
particular instance by the Robot who is least busy,

and so on.

Conversely, one implementation object may support several otherwise unrelated
interfaces concurrently, or different interfaces across time. For
example, a Person object might play the roles (hence ``export''
the interfaces) of:

OfficeManager,

Parent,

Driver,

Customer,

and so on.

A good way to think about such relationships is to treat roles as
channels to objects, where each role supplies particular
clients with a coherent view of a set of capabilities:

Applicability

It is hard to predict when you will need to rely on the
flexibility and scaffolding provided by roles, so a conservative
default strategy is to always start with roles.

Many (but not all) constructs normally presented in the context
of OO analysis (versus design) are more profitably construed as
applying to roles, not objects. Thus, many role-centric development steps
may be thought of as a particular approach to OOA.

You can apply this pattern retrospectively on an existing design.
This extends the idea of refactoring concrete classes to be
subclasses of abstract classes.

In ``open'' systems of distributed objects, you can almost never
commit to object implementations before run-time, so have little
choice but to take this path. In particular, the CORBA model of ``objects'' seems to
make more sense as a model of roles. Most of the ``visible''
aspects of CORBA applications (including IDL interfaces) rely on
role-based development.

In some development efforts, the level of abstraction provided by
roles is just superfluous. Sometimes you can ``see'' the right
objects from the start and know that they will never share or change
roles, so you might as well jump into class and object based
development. This happens for example when software objects are used
to model naturally occurring objects in a one-to-one fashion.

Role-based development probably isn't very useful in relatively
small self-contained systems and exploratory programming contexts in
which the consequences of throwing away classes, backtracking, and
the like don't cost or matter much.

Don't deal with roles in contexts where you must be dealing with
objects. For example, in a fault-tolerant application, the decision
about how to place redundant objects on different processors is an
object-level design task.

Design Steps

Once you have decided to adopt a role-based scheme, you need to
delve into the details. The following entries describe the two main
facets:

[UNWRITTEN PATTERN] Describing Roles

[UNWRITTEN PATTERN] Mapping Roles to Objects

Background

There have been quite a few attempts recently to integrate various
conceptualizations of roles with mainstream OO development. From
another view, roles have emerged as a way of making sense of and
extending common techniques for separating interfaces from
implementations.
The current patterns describe a variant associated with the
role-based specification framework described in our PSL paper. The paper
includes some brief surveys, comparisons, and references to related
work, including: