For example, if Student and Employee are defined as Role Classes of class Person, and Student and Employee were defined as being in the same Role Group, then the Person object would be able to play the role of one or more Employees or one or more Students and migrate between playing the Role of a Student or an Employee dynamically.
Depending on how you group the Role classes, set the cardinality constraints and define the migration rules will role playing by the Person class be defined.
For example, you could define a Person that playes the Role of either a Student or an Employee. To do this both the Student Role and the Employee Role would be defined as being in the same Role Group. Consider this class dictionary:
Person =Note the common part shared by Student and Employee, the playedBy member. This is a pointer back to the object that is playing the Role.<personRole>PersonRoleGroup<age>Number . PersonRoleGroup : Student | Employee *common*<playedBy>Person . Student = . Employee = .
Assume that there is a method called PlayedBy() of the PersonRoleGroup that returns the pointer to the Person object and a method called GetAge() of the Person class.
The method PlayedBy() allows access to properties of the Person object from a Role that is being played by the Person. For example, determining the age of a student would be written as:
pStudent->PlayedBy()->GetAge()A Person object can also have the role it is playing migrated from one role to the other. This would be written as:
// create an 18 year old person who // is playing the role of a Student Person * pPerson( new Student(), 18 ); ... // the Person is now employed and no longer a Student delete pPerson->rset_personRole( new Employee() );The method rset_personRole() takes the new role for the Person object to play and returns the previous role. Note the deletion of the return value form rset_personRole(). This is to insure that the Student object is deleted.
Role Groups have the following characteristics:
Each Role class defines a data member called playedBy that is a pointer to the Person playing the Role. Consider the following class dictionary that defines a player Person and a Role Group that represents the marriage status of the Person:
Person =Using this structure, a Person can play only 1 Role from the Role Group MarriageStatus.<marrigeRole>MarriageRole . MarriageRole : Single | Married | Divorced | Widowed *common*<playedBy>Person . Single = . Married = . Divorced = . Widowed = .
Both cardinality and migration rules can not be expressed in the class dictionary. They must be expressed in the Role Playing Propagation Patterns defined by an adaptive program. (See the Demeter book for more information about Demeter.)
Role Classes are stored in a list when it is necessary to allow the Player to play a Role multiple times in an instance allowing for cardinality from 0 to N.
Migration rules define how a Role can migrated to other Roles in the same Role Group. For example, a program would have to make sure that if a Person were playing the Role of a Single Person, then they could not migrate from playing the Single Role to playing the Divorced or Widowed Roles.
An adaptive program, defined as a set of Propagation Patterns, must define the cardinality and migration rules as attributes of the application domain.
Now lets add two more Role Groups, the EmployeeRole Role Group and the StudentRole Role Group. A Person can hold multiple jobs so we must allow for the EmployeeRole to be played multiple times by the same Person.
A Person is either a Student or not so there is no need to store Student Roles in a list. We want to allow a Person to be employed and to go to school so the StudentRole and EmployeeRole must be in different Role Groups.
The following is the complete legal class dictionary:
Person = <marriageRole> MarriageRole ";"
<employmentRole> EmploymentRole ";"
<studentRole> StudentRole.
MarriageRole : Single |
Married |
Divorced |
Widowed *common* [ <playedBy> Person ] .
Single = "single" .
Married = "married" .
Divorced = "divorced".
Widowed = "widowed" .
EmploymentRole : Employed |
UnEmployed *common* [ <playedBy> Person ] .
UnEmployed = "unemployed" .
Employed = <jobTypeList> List( JobType ) .
JobType : PartTimeJob |
FullTimeJob *common* "shift=" <shift> DemNumber .
PartTimeJob = "part-time-job" .
FullTimeJob = "full-time-job" .
StudentRole : Student |
NonStudent *common* [ <playedBy> Person ] .
Student = "student" .
NonStudent = "non-student" .
List(S) ~ S { "," S } .
Note the use of Adaptive Dynamic Subclasses to define the
Employed Role. This allows a Person who is playing the Role
of a part-time employee to migrate that job to a full-time
position dynamically or vice versa.
Now if we were developing an adaptive program using this class dictionary, we first would want to set the cardinality constraints for the role playing and then define the migration rules within the role groups.
The cardinality constraints are:
The migration rules for the MarriageRole are:
The following are the Role Playing Propagation Patterns that an adpative program would include as a part of its domain for enforcing the cardinality constraints and migration rules that we have set up:
*operation* void PlaySingleRole()
*wrapper* Person
(@
if ((strcmp(marriageRole->get_type(),"Married") == 0) ||
(strcmp(marriageRole->get_type(),"Divorced") == 0) ||
(strcmp(marriageRole->get_type(),"Widowed") == 0))
{
cout << "Can't change role from married, divorced or widowed to single" << endl;
return;
}
else
{
delete rset_marriageRole( new Single() );
marriageRole->set_playedBy(this);
cout << "Playing single role now" << endl;
}
@)
*operation* void PlayMarriedRole()
*wrapper* Person
(@
delete rset_marriageRole( new Married() );
marriageRole->set_playedBy(this);
cout << "Playing married role now" << endl;
@)
*operation* void PlayDivorcedRole()
*wrapper* Person
(@
if ((strcmp(marriageRole->get_type(),"Widowed") == 0) ||
(strcmp(marriageRole->get_type(),"Single") == 0))
{
cout << "Can't change role from single or widowed to divorced" << endl;
return;
}
else
{
delete rset_marriageRole( new Divorced() );
marriageRole->set_playedBy(this);
cout << "Playing divorced role now" << endl;
}
@)
*operation* void PlayWidowedRole()
*wrapper* Person
(@
if (!(strcmp(marriageRole->get_type(),"Married") == 0))
{
cout << "Can't change role from single or divorced to widowed" << endl;
return;
}
else
{
delete rset_marriageRole( new Divorced() );
marriageRole->set_playedBy(this);
cout << "Playing widowed role now" << endl;
}
@)
*operation* void PlayUnEmployedRole()
*wrapper* Person
(@
delete rset_employmentRole( new UnEmployed() );
employmentRole->set_playedBy(this);
cout<< "Playing unemployed role now" << endl;
@)
*operation* void PlayEmployedRole( JobType * pJobType )
*wrapper* Person
(@
if (strcmp(employmentRole->get_type(),"Employed") == 0)
{
cout << "Checking employment status..." << endl;
DemNumber * pNumJobs = new DemNumber(0);
employmentRole->NumberOfJobs( pNumJobs );
cout << "Number Of Jobs = " << pNumJobs << endl;
if (pNumJobs->get_val() == 3)
{
cout << "Maximum number of jobs reached, can not work another!" << endl;
return;
}
DemNumber * bConflict = new DemNumber(0);
employmentRole->ShiftConflict( pJobType, bConflict );
if (bConflict->get_val() == 1)
{
cout << "There is a shift conflict with this new job!" << endl;
return;
}
employmentRole->AddJob( pJobType );
cout << "Playing employed role now" << endl;
}
else
{
delete rset_employmentRole( new Employed() );
employmentRole->set_playedBy(this);
employmentRole->AddJob( pJobType );
cout << "Playing employed role now" << endl;
}
@)
*operation* void PlayStudentRole()
*wrapper* Person
(@
delete rset_studentRole( new Student() );
studentRole->set_playedBy(this);
cout << "Playing student role now" << endl;
@)
*operation* void PlayNonStudentRole()
*wrapper* Person
(@
delete rset_studentRole( new NonStudent() );
studentRole->set_playedBy(this);
cout << "Playing non-student role now" << endl;
@)
*operation* void AddJob( JobType * pJobType )
*traverse*
*from* EmploymentRole
*bypassing* -> *,playedBy,*
*to-stop* Employed
*wrapper* Employed
(@
cout << "Adding a job" << endl;
if (jobTypeList == NULL)
{
set_jobTypeList( new JobType_List() );
}
(get_jobTypeList())->append( pJobType );
@)
*operation* void NumberOfJobs( DemNumber * pCount )
*traverse*
*from* EmploymentRole
*bypassing* -> *,playedBy,*
*to-stop* Employed
*wrapper* Employed
(@
cout << jobTypeList->list_length() << endl;
pCount->set_val(jobTypeList->list_length());
@)
*operation* void ShiftConflict( JobType * pJobType, DemNumber * bConflict )
*traverse*
*from* EmploymentRole
*bypassing* -> *,playedBy,*
*to-stop* JobType
*wrapper* JobType
(@
if (bConflict->get_val() == 1)
{
cout << "Conflict found" << endl;
return;
}
else
{
bConflict->set_val((this->get_shift() == pJobType->get_shift()));
}
@)
cout << "\n*** Testing Role Playing Propagation Patterns ***" << endl; cout << "Change marriage role from single to married." << endl; iPerson->PlayMarriedRole(); cout << "Change student role to non-student role." << endl; iPerson->PlayNonStudentRole(); cout << "Change marriage role from married to divorced." << endl; iPerson->PlayDivorcedRole(); cout << "Change marriage role from divorced to single." << endl; cout << "This should produce an error message.\n" << endl; iPerson->PlaySingleRole(); cout << "Change unemployed role to employed with 3 jobs." << endl; FullTimeJob * pFullTimeJob = new FullTimeJob(); pFullTimeJob->set_shift(new DemNumber(1)); iPerson->PlayEmployedRole(pFullTimeJob); PartTimeJob * pPartTimeJob1 = new PartTimeJob(); pPartTimeJob1->set_shift(new DemNumber(2)); iPerson->PlayEmployedRole(pPartTimeJob1); PartTimeJob * pPartTimeJob2 = new PartTimeJob(); pPartTimeJob2->set_shift(new DemNumber(3)); iPerson->PlayEmployedRole(pPartTimeJob2); cout << "Now try to add a 4th job." << endl; cout << "This should produce an error message." << endl; PartTimeJob * pPartTimeJob3 = new PartTimeJob(); pPartTimeJob3->set_shift(new DemNumber(2)); iPerson->PlayEmployedRole(pPartTimeJob3);This is the output produced by the test code:
*** Testing Role Playing Propagation Patterns *** Change marriage role from single to married. Playing married role now Change student role to non-student role. Playing non-student role now Change marriage role from married to divorced. Playing divorced role now Change marriage role from divorced to single. This should produce an error message. Can't change role from married, divorced or widowed to single Change unemployed role to employed with 3 jobs. Adding a job Playing employed role now Checking employment status... Number Of Jobs = 1 Adding a job Playing employed role now Checking employment status... Number Of Jobs = 2 Adding a job Playing employed role now Now try to add a 4th job. This should produce an error message. Checking employment status... Number Of Jobs = 3 Maximum number of jobs reached, can not work another!