Role Modeling

Source: comp.object
Date: 22-Jun-98

Related Sites


------------------------------

o-< Problem: How can the concept of "roles" be modeled using objects?


---------------

o-< Eric Hermanson presented the problem:

For instance, let's say you're modeling a hospital and you want objects for "Person", "Nurse", "Patient", "Doctor", etc. Interestingly, a "Person" can be both a "Nurse" and a "Patient" at the same time (a Nurse gets hurt and is admitted to the hospital).

You wouldn't really want Nurse and Patient objects inheriting from "Person" because then you'd have to instantiate two different objects (one for Nurse, one for Patient) for the same person [...]. In my opinion, it would be better if that person could adhere to two roles at the same time - one for Nurse and one for Person.


---------------

o-< Amit Patel showed a solution:

One thing that I worry about if I see inheritance used to express roles is that roles can change over time. For example, perhaps the Nurse got hit by lightning and became a Patient, or the Doctor decided to retire.

[...]

What I came up with was that the Person object should not be related by inheritance to Nurse, Patient, Doctor, etc. Instead, each role should be a separate object that is associated with the person object. The person object would keep a list of roles, and each role would know which person is playing that role.

For example, there would be an Amit object representing me. Since I'm a Student, there would also be a Student object associated with the Amit object. The student object would contain my student ID, my grades, my enrollment history, and so on. If I fall off my bike and go to the hospital, I am now playing the role of student and patient. The Amit object does not change, except that I'd have to create a new Patient object to represent my being a patient -- patient ID, medical history, list of allergies, treatment, etc. When I leave the hospital, the patient object can be destroyed or archived. If I enter medical school, I might be a Nurse for a while, so I'd create a nurse object. If I graduate, the student object would be destroyed or archived, but I would still be a nurse until I stop playing that role.

This approach allows you to play many roles at once, and to enter and leave roles as needed. I can be a student, a teacher, a doctor, a patient, a passenger, and a volunteer all at once. It also allows me to play two roles belonging to the same 'class' -- for example, if I'm a student at two different colleges, I'd want two student objects to record the student IDs and classes I'm taking at each college.

In general these kinds of things are hard to do with multiple inheritance -- you'd need to be able to remove and add superclasses at run-time (aieeeeee!). You'd also need some way of ensuring that there are no clashes between fields (for example, Doctor.ID has the same name as Student.ID). And you'd need a MI system that allows you to inherit from the same class many times (to handle the case where you are a student at many places).

The separate-objects approach makes message sending more complicated, because you have to direct messages to the role objects yourself. [...] On the other hand, MI might not give you what you need to figure out how to send a message like "turn in final exam" -- the method that needs to be called depends on which class the exam is for, so some user handling of messages may be necessary anyway.

[...] I keep thinking there's a design pattern that can deal with it but I haven't seen one yet. :)


---------------

o-< Matthias Hoelzl pointed to some relevant patterns:

Extension Object combined with Product Trader [both from PLoPD3].

Extension Object [Also known as Facet]

Intent:
Anticipate extensions to an object's interface. Extension Object lets you add interfaces to a class and lets clients choose and access the interfaces they need.

Applicability:
Use the Extension Object Pattern when

  • You want to add new or unforeseen interfaces to existing classes and you don't want to impact clients that don't need this new interface. Extension Object lets you keep related operations together by defining them in a separate class.
  • Clients perceive different roles for the same abstraction, and the number of such roles is open-ended.
  • A class should be extensible without being sub-classed directly.
Product Trader [Also known as Late Creation]

Intent:
The Product Trader design pattern allows clients to create objects by naming an interface and by providing a specification. A Product Trader decouples the client from the product and thereby eases the adaptation, configuration and evolution of class hierarchies, frameworks and applications.

Applicability:
Use a Product Trader if

  • You want to dynamically select a product class according to selection criteria available only at run time (dynamic selection argument)
  • [...]
This combination would allow you to encapsulate the current role in the extension object and select the right extension object via a predicate passed to the product trader. However the complexity of the resulting design might be quite high.


---------------

o-< In fact, this combination of patterns was documented as an independent pattern:

See The Role Object Pattern by Dirk Bäumer, Dirk Riehle, Wolf Siberski, and Martina Wulf.

Intent:
Adapt an object to different client's needs through transparently attached role objects, each one representing a role the object has to play in that client's context. The object manages its role set dynamically. By representing roles as individual objects, different contexts are kept separate and system configuration is simplified.

Applicability:
Use the Role Object Pattern if:

  • you want to handle a key abstraction in different contexts and you do not want to put the resulting context-specific interfaces into the same class interface.
  • you want to handle the available roles dynamically so that they can be attached and removed on demand, that is at runtime, rather than fixing them statically at compile-time.
  • you want to treat the extensions transparently and need to preserve the logical object identity of the resulting object conglomerate.
  • you want to keep role/client pairs independent from each other so that changes to a role do not affect client that are not interested in that role.

------------------------------

o-< More Info:

Martin Fowler, Dealing with Roles

T Reenskaug, Wold Reenskaug and Lehne Reenskaug,
Working With Objects: The OORAM Software Engineering Method
(presents Object-Oriented Role Analysis Modelling - thanks to Eirik Mangseth for the reference)


------------------------------