Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Role Object pattern question

I am looking at a paper done by Martin Fowler called Dealing With Roles. In it, Fowler breaks out three basic strategies for dealing with roles for a Person in an organization (ie, Employee, Engineer, Manager, Salesman) which include Role Subtyping, Role Object, and Role Relationships.

Having been written in 1997 it is certainly old, and being a "working draft" it also has some errors that otherwise wouldn't be there. I am confused going over an example of Role Object he goes through, and have included a my c# interpretation of some of his java code below.

I have three questions:
(1) there's a lot of type identification being done with strings that seems like it should be replaceable with generics, but I can't get a handle on how to do so yet. How would you implement this code using generics?
(2) JobRole is in the code as the string name for a type but it is not specifically defined with the rest of the code. I can't tell if this is a base class for PersonRole or not. What is the definition of JobRole? Does the unit test look like a correct example of the pattern's usage?
(3) Does anyone have any links towards a more recent implementation and example of using a Role Object?

Cheers,
Berryl

public class PersonWithRoles : Person
{
    private readonly IList<PersonRole> _roles = new List<PersonRole>();

    public static PersonWithRoles CreatePersonWithRoles(string identifierName) {
        ...
    }

    public void AddRole(PersonRole role) { _roles.Add(role); }

    public PersonRole RoleOf(string typeName) { return _roles.FirstOrDefault(x => x.HasType(typeName)); }
}

public class PersonRole
{
    public virtual bool HasType(string typeName) { return false; }
}

public class Salesman : PersonRole
{
    public override bool HasType(string typeName)
    {
        if (typeName.Equals("Salesman", StringComparison.InvariantCultureIgnoreCase)) return true;
        if (typeName.Equals("JobRole", StringComparison.InvariantCultureIgnoreCase)) return true;

        return base.HasType(typeName);
    }

    public int NumberOfSales { get; set; }

}

[TestFixture]
public class RoleUsageTests
{
    [Test]
    public void Test() {
        var p = PersonWithRoles.CreatePersonWithRoles("Ted");
        var s = new Salesman();
        p.AddRole(s);

        var tedSales = (Salesman) p.RoleOf("Salesman");
        tedSales.NumberOfSales = 50;
    }
}
like image 743
Berryl Avatar asked Jun 27 '11 22:06

Berryl


2 Answers

I am under the belief that these types of applications are good candidates for the use of the decorator design pattern in which there is a Person base class, and then each role extends that base class. The base class has no declarations of permissions -- only the roles classes that extend person should.

Sorry to be vague, but I hope you get what I'm trying to describe.

class programmer {
 name ...
 email ...
 seat location ...
}

class html_coder extends programmer {
 canCodeHTML ...
}

class script_coder extends programmer {
 canCodeHTML ...
 canCodeJavascript ...
}

class senior_developer extends programmer {
 canCodeHTML ...
 canCodeJavascript ...
 canAccessDatabase ...
 canEditProjectArchitectureFiles ...
 canWearTennisShoesToWork...
}

these all extend the programmer base class... notice that the programmer class does not declare rights... just properties

like image 168
Kristian Avatar answered Oct 20 '22 14:10

Kristian


  1. Generics in c# can help make a neater implementation
  2. JobRole is a sub-type of PersonRole, and a super-type for a specific jobs

I'd still like to see examples of usage, as one of Fowler's points about this pattern is that the typing flexibility comes at the expense of a two step usage pattern. Implementing this using a Decorator pattern does not change this. For example, to work with a Person in the Role of Salesman, first you need to get an instance of a person and then find the role of Salesman.

Cheers,
Berryl

public class Person
{
    public FullName FullName  { get; set; }
    public IList<IRole> Roles { get; private set; }

    public Person(FullName fullName) => FullName = fullName;

    public IRole GetRoleOf<T>() where T: IRole => 
        Roles.FirstOrDefault(x => x.HasType(typeof(T)));
    public void AddRole(IRole role)    => Roles.Add(role);
    public bool RemoveRole(IRole role) => Roles.Remove(role);

}

public interface IRole
{
    bool HasType(Type type);
}

public abstract class Role : IRole
{
    public virtual bool HasType(Type type) { return false; }
}

// Base type for any type of role for a person.
public class PersonRole : Role
{
    public override bool HasType(Type type) => type.Equals(typeof(PersonRole));
}

// Base type for any type of role for a person.
public class JobRole : Role
{
    public override bool HasType(Type type) 
    {
        if (type.Equals(GetType())) return true;
        return base.HasType(type);
    }
}

// Behavior specific to a salesman
public class Salesman : JobRole, ISalesman
{
    public override bool HasType(Type type)
    {
        if (type.Equals(GetType())) return true;
        return base.HasType(type);
    }

    public int NumberOfSales { get; set; }
}

[TestFixture]
public class JobRoleTests : BaseTestFixture
{
    private PersonEx _person;

    protected override void OnFixtureSetUp() 
    {
        _person = new PersonEx(new OneNameFullName("schmuck"));
        // can be a Salesman
        _person.AddRole(new Salesman());
    }

    [Test]
    public void Salesman_CanGet() 
    {
        var salesman = _person.GetRoleOf<Salesman>() as Salesman;
        Assert.That(salesman, Is.Not.Null);
        salesman.NumberOfSales = 50;
        Assert.That(salesman.NumberOfSales, Is.EqualTo(50));
    }
}
like image 39
Berryl Avatar answered Oct 20 '22 15:10

Berryl