1

I'm working on a project that requires several different users in the system, all of which have overlapping responsibilities; we've isolated two possible ways of tackling this problem, but we're unsure which of them are the most correct way of going about it.

Option 1: Create a virtual super-class for all the user-classes to inherit from. This super-class will have a boolean field for each of the responsibilities, allowing us to toggle which of the sub-classes have which responsibility.

  • Pros
    • One single class for all the sub-classes to inherit from, letting us toggle responsibilities via boolean fields
  • Cons:
    • It's essentially a god-class, which means it holds too much responsibility

Option 2: Create nineteen (19) interfaces, each representing one of the available responsibilities, and then have each of the user-classes inherit from this pool of interfaces in order to determine their responsibilities.

  • Pros
    • It allows us to seperate the concerns into several interfaces
  • Cons
    • We'll be in over our heads with interfaces, making it difficult to keep track of

So which is the best way of doing it, or is there perhaps a better way?

To elaborate my question I'm including the graphic below; it showcases the relations between the different parts of the system and the different users.

enter image description here

R means read access and RW means read + write access.

As you can see it's a bit of a mess of interlacing attributes.

Electric Coffee
  • 1,465
  • 1
  • 14
  • 22
  • 4
    Composition over inheritance. You're welcome. – Silviu Burcea Oct 19 '15 at 08:45
  • 1
    @SilviuBurcea that tells me nothing – Electric Coffee Oct 19 '15 at 08:45
  • 1
    Instead of having tons of interfaces to implement, use composition. Instead of storing tons of booleans, store more meaningful objects, like permissions and make a list of them. Another approach, if you like booleans, is a permission list implemented as a BitSet. – Silviu Burcea Oct 19 '15 at 08:48
  • 1
    @SilviuBurcea define composition – Electric Coffee Oct 19 '15 at 08:49
  • I almost did it, instead of inheriting things, store them as properties. I just gave you an alternative for storing tons of booleans as properties, make a list of them and give them more semantics. You may want to Google the concept composition over inheritance, you will find plenty of examples. – Silviu Burcea Oct 19 '15 at 08:52
  • And if I have to pick an option, I'd pick #3: Compressed version of option 1. It is not a god class, you, as a human being, have some physical properties and own some properties. They are both represented by HAS-A relationship(composition). – Silviu Burcea Oct 19 '15 at 08:55
  • @SilviuBurcea by properties I assume you don't mean encapsulated fields... So what do you mean then? – Electric Coffee Oct 19 '15 at 08:59
  • both answers are good, David also added some code, this is what I had in mind in the morning. Instead of 19 classes, I'd only create one with name as parameter, named Resource. Create roles(technician, sales, etc.) and assign permissions(RWX) to resources. Your user can now have 1(or more, depending on your business model) role. If an user can have more than 1 roles, be sure to think about how do you handle the actual permissions. I.e. what's the permission for an user that's both sales and technician? Hope this clears the clouds :) – Silviu Burcea Oct 19 '15 at 18:49

2 Answers2

3

Option 3: Create individual objects corresponding to specific responsibilities, and then have each user hold a collection of these.

The collection of responsibilities for a user will contain only the responsibilities assigned to the user, i.e. you don't need an object to indicate the lack of a responsibility.

You worry it is "overkill" to create 19 classes for 19 responsibilities, but actually it is the simplest solution. If you indicate the same with 19 boolean fields on the base class, you have the same number of semantic entities, but a much more complex system overall, since each new responsibility increases the complexity of all user classes. By having the responsibilities as objects separate from the user, a new responsibility will not affect the user classes at all, but only the specific parts of the program which check for this specific responsibility.

Also, you can easily separate the assignment of responsibilities into a configuration. This would be quite tricky with option 1 and impossible with option 2.

Note: If you need to distinguish between read and read/write you need more that 19 boolean fields or 19 interfaces. For option 1 you need tri-state enumerations rather than booleans. For option 2 you need 38 distinct interfaces. For option 3 you can have a boolean flag on each responsibility which indicate if it is read or read/write.

JacquesB
  • 57,310
  • 21
  • 127
  • 176
  • wouldn't that create a ton of classes instead? – Electric Coffee Oct 19 '15 at 09:10
  • 2
    @ElectricCoffee: One for each responsibility - but you get the same complexity with the other proposed solution, except in option 1 and 2 you let the complexity infect the user classes (with an explosion of flags or interfaces), here it is nicely separated. – JacquesB Oct 19 '15 at 09:13
  • I've expanded my question, elaborate your answer? – Electric Coffee Oct 19 '15 at 12:52
  • @ElectricCoffee: I would be happy to. What in particular do you find unclear in the answer? Or do you have further questions? – JacquesB Oct 19 '15 at 14:37
  • Well since it's one class for each responsibility, and there are 3 variants of each responsibility, it leads mme to believe I have to create 57 different classes, which seems a little beyond overkill. So I'm just wondering how it would make most sense to go about this – Electric Coffee Oct 19 '15 at 14:51
  • @ElectricCoffee: I have expanded the answer. You only need two variants of each responsibility (R and RW) and you can solve that with a boolean flag on the responsibility. Your option 1 and 2 also get more complex if you have to take R vs RW into consideration. – JacquesB Oct 19 '15 at 18:18
  • Then what would I do for the third case? Where it's neither R or RW? – Electric Coffee Oct 19 '15 at 18:22
  • Then the user doesn't have that responsibility at all. – JacquesB Oct 19 '15 at 18:25
  • could you provide a concrete example? – Electric Coffee Oct 19 '15 at 18:27
  • @ElectricCoffee: Sure, what exactly would you like an example of? – JacquesB Oct 20 '15 at 08:12
  • Just an example of how one of the classes would be composed – Electric Coffee Oct 20 '15 at 08:17
3

I'd opt for option 3, use composition.

Depending on what you are doing with the responsibilities, you might be able to use an enum for them:

public enum Responsibilities
{
    Responsibility1,
    Responsibility2,
    ...
}

Then your user class might be something like:

public class User
{
    private List<Responsibilities> _responsibilities =
        List<Responsibilities>();

    public void AddResponsibility(Responsibilities responsibility) =>
        _responsibilities.Add(responsibility);

    public bool HasResponsibility(Responsibilities responsibility) =>
        responsibilities.Contains(responsibility);
}

If your need logic behind each responsibility, replace the enum with an IResponsibility interface and have each responsibility class implement that interface.

David Arno
  • 38,972
  • 9
  • 88
  • 121