Role And Player

A design pattern.

Define Role objects that implement state and behavior specific to some role in the ProblemDomain. The purpose is to separate the state and behavior of the role type from that of the player type, so that the two may vary independently.

Since roles are played by something, Role objects incorporate an instance of some type (typically a Party; see Chapter 2 of AnalysisPatterns) as their "player". Role objects decorate player objects, but they differ from Decorators in that a Role's public interface is a superset of a player's public interface, and clients are aware of the Role's existence but unaware of the player's existence.

Roles may also function as players or, stated another way, roles may have roles. For example, one might decide that SystemAdministrator is a role of a User which, in turn, is a role of a Person. The decision to reify SystemAdministrator as a role of User is reasonable if SystemAdministrator has role-specific state and/or behavior, and the public interface of SystemAdministrator is a superset of the public interface of User. In practice, however, such "nesting" of roles seems to occur relatively infrequently.

The most prolific documentor of this pattern is PeterCoad (in the recent JavaModelingInColorWithUml?, and as far back as 1991 in a CACM article). Anyway, the purpose is to separate the behavior of the role type from that of the player type, so that the two may vary independently. Here's an example: suppose you have the abstractions Buyer, Seller, Person, and Organization in your problem domain, where Persons and Organizations can both be Buyers and Sellers? How do you relate the implementations of these abstractions in a single-inheritance environment? The solution is to let Buyer and Seller be roles of Person and Organization (the players). This separates "Buyer-ness" (state and behavior) and "Seller-ness" from "Person-ness" and "Organization-ness" - but any message a Person or Organization understands should also be understood by Buyer and Seller. In Smalltalk this was easy by overriding #doesNotUnderstand: to delegate the misunderstood message to the player. But in Java you have to introduce interfaces and write piles of brain-dead delegating methods in the Role classes. Anyway, the benefit is that your players are reusable across applications, while your roles tend to be application-specific.

Some other benefits are that you can accomplish many of the benefits of MultipleInheritance. For example, a player that represents a matrix can be treated as either a collection or an ArithmeticObject?, depending on the role chosen for it.

The #doesNotUnderstand mechanism in Smalltalk was still subject to many problems in the presence of bugs and mis-casts. RoleAndPlayer is thus even helpful in a SmalltalkEnvironment?. It's effect is similar to changing the class pointer of an instance -- sometimes it is precisely the right thing.


My experience is that there are cases when applying role and player definitely isn't the SimplestThing, or that YouArentGonnaNeedIt. For example, if the only role of Person that I can think of in my problem domain (without unwarranted speculation about future requirements) is User, then reifying User as a role of Person may be extra work that I don't need to do.

Primarily, I see this as a ROI decision. Implementing role and player in Java takes a lot more effort than it did in Smalltalk, and it's use may even increase the complexity of an application. On the other hand, this pattern reflects the way I see the world, so for me it's the most comfortable, natural and traceable way to implement certain problem domain abstractions.

A reasonable question, then, is what heuristics should one use to decide when to apply the role and player pattern? I offer the following candidate heuristics:

  1. there is some "critical mass" of candidate role types in an application; or
  2. one wants to define standard Party, Person and Organization implementations that can be reused across applications; or
  3. you already have a working role and player framework implementation in your toolbox; or
  4. you have objects in your application that seem to change their type from one application context to another; or
  5. you believe it is more reflective of the way the world really works.

With respect to heuristic #1, you'll have to decide for yourself what this threshold is. For me, it's a pretty low number because of heuristic #3 - my toolbox already contains a working implementation of the role and player pattern which means that writing a role is an incremental programming task. I just have to write the role-specific code and I avoid having to write the player-specific code, which generally *saves* me a lot of time!

Another question is how can I decide whether a type ought to be implemented as a role of some player? My heuristics at this point are:

  1. the candidate role's PublicInterface must be a superset (a further extension) of it's player's interface; and
  2. the candidate role must have role-specific state or behavior.

Otherwise, it's not a role, but simply some object that is associated with the player.

--DaveMuirhead


Contributors: RandyStafford, TomStambaugh, RichardDrake, DaveMuirhead

See also RolePattern, RoleObjectPattern


CategoryPattern


EditText of this page (last edited August 30, 2005) or FindPage with title or text search