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,
Berrylpublic class PersonWithRoles : Person
{
private readonly IList<PersonRole> _r开发者_Python百科oles = 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;
}
}
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
- Generics in c# can help make a neater implementation
JobRole
is a sub-type ofPersonRole
, 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));
}
}
精彩评论