A dynamic role switching pattern

For many applications static type hierarchies do not provide the flexibility needed to model dynamically changing roles. For example we may wish to consider a person as an actor capable of various roles during his lifetime, some of which may even coexist concurrently. The characteristic feature of the dynamic role switching pattern is that it allows us regard a particular entity from multiple perspectives and that the behavior of that entity changes accordingly. We will look at a possible realization of the pattern below.

Taking our view of a person as an actor as a starting point, we need first to establish the repertoire of possible behavior.


The Actor Class

    enum Role { Person = 0 , Student, Employer, Final };
    
    class actor {                      
defines the repertoire

public: actor() { } virtual void walk() { if (exists()) self()->walk(); } virtual void talk() { if (exists()) self()->talk(); } virtual void think() { if (exists()) self()->think(); } virtual void act() { if (exists()) self()->act(); } virtual void become(Role) { }
only for a person

virtual void become(actor*) { } virtual actor* self() { return this; }
an empty self

int exists() { return self() != this; }
who ami

};

slide: The Actor Class

Apart from the repertoire of possible behavior, which consists of the ability to walk, talk, think and act, an actor has the ability to establish its own identity (self) and to check whether it exists as an actor, which is true only if it has become another self. However, an actor is not able to assume a different role or to become another self. We need a person for that!

Next, we may wish to refine the behavior of an actor for certain roles, such as for example the student and employer roles, which are among the many roles a person can play.


The Student Actor

    class student : public actor {
    public:
    void talk() { cout << "OOP" << endl; }
    void think() { cout << "Z" << endl; }
    };
    
    class employer : public actor {
    public:
    void talk() { cout << "$$" << endl; }
    void act()  { cout << "business" << endl; }
    };
  

slide: The Student Class

Only a person has the ability to assume a different role or to assume a different identity. Apart from becoming a Student or Employer, a person may for example become an adult_person and in that capacity again assume a variety of roles.

The Person is an Actor

    class person : public actor {
    public:
    
    person();                         
to create a person

void become(Role r);
to become a ...

void become(actor* p);
change identity

int exists() { return role[Person] != this; } actor* self() { return exists()?role[Person]->self():role [role]; } private: int _role; actor* role[Final+1];
the repertoire

};

slide: The Person is an Actor

A person may check whether he exists as a Person, that is whether the Person role differs from the person's own identity. A person's self may be characterized as the actor belonging to the role the person is playing, taking a possible change of identity into account. When a person is created, his repertoire is still empy.

The Repertoire

    person::person() {
       for (int i = Person; i <= Final ; i++ ) role[i] = this;
       become( Person );
       }
  

slide: The Repertoire

Only when a person changes identity by becoming a different actor (or person) or by assuming one of his (fixed) roles, he is capable of displaying actual behavior.

Changing Roles

    void person::become(actor* p) { role[Person] = p; } 
permanent

void person::become(Role r) { require( Person <= r && r <= Final ); if (exists()) self()->become(r); else { _role = r; if ( role [role] == this ) { switch (_role) { case Person: break;
nothing changes

case Student: role [role] = new student; break; case Employer: role [role] = new employer; break; case Final: role [role] = new actor; break; }; } } }

slide: Changing Roles

Assuming or 'becoming' a role results in creating a role instance if none exists and setting the _role instance variable to that particular role. When a person's identity has been changed, assuming a role takes effect for the actor that replaced the person's original identity. (However, only a person can change roles!) The ability to become an actor allows us to model the various phases of a person's lifetime by different classes, as illustrated by the adult_person class.

Becoming adult

    class adult_person : public person {
    public:
      void talk() { cout << "interesting" << endl; }
    };
  

slide: Becoming adult

In the example code below we have a person talking while assuming different roles. Note that the person's identity may be restored by letting the person become its original self.
    person p; p.talk();                    
empty

p.become(Student); p.talk();
OOP

p.become(Employer); p.talk();
$$

p.become(new adult_person); p.talk();
interesting

p.become(Student); p.talk();
OOP (new student)

p.become(&p); p.talk();
$$ (old role)

p.become(Person); // initial state

slide: Becoming one's self

The dynamic role switching pattern can be used in any situation where we wish to change the functionality of an object dynamically. It may for example be used to incorporate a variety of tools in a drawing editor, as illustrated in  [Hush].