Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modelling "I'm a * but I'm also a **"

In [this post], I'm struggling to implement a State Pattern as @jonp suggests. I don't quite get how to use what's he's posted but it leads to the thought that maybe I'm trying to fit a square peg into a round hole. So my question:

If I have a visitor to my site that can play multiple roles i.e. a User could be a Vendor, an Employer, an Advertiser, OR all of the above, should I be using inheritance? I've declared:

class Vendor : User {}
class Advertiser : User {}

et cetera, but when a user is both a vendor and an employer then instances of different classes really point to the same underlying object... I'm not sure this can work. How do I model it?

* update *

thanks everyone (you all get a point (it's all I can give)). I've been pulling my hair out over deep-copies with EF, downcasting and the state pattern for the last several days. The role approach makes much more sense.

like image 426
ekkis Avatar asked Sep 13 '11 00:09

ekkis


3 Answers

This sounds like a situation to which the attribute pattern (or so I call it) would be very appropriate. It's a much more loosely-coupled approach than simple inheritance that can be used to specify multiple "behaviours" or in your case kinds of User. It's really nothing more complicated than an object having tags of another kind of object.

The easiest way to implement it would be to have a concrete User class, with a read-only property IList<UserRole> (internally this can be a List<T> field perhaps). Your UserRole class would then be abstract, and VendorRole/AdvertiserRole/etc. would derive from it, allowing you to tag on an arbitrary number of different roles (even ones of the same type) onto a given user. These roles can in addition define their own custom behaviours, utility methods, etc.

In addition, you could define a GetRole<TRole> method on your User class to facilitate access to roles of a specific type (assuming each User only has a single Role of a specific subtype).

Side note: you may also consider the decorator patern, which is closely related to the above mentioned pattern -- though personally I feel it is overkill here, and really adds nothing in terms of flexibility or power. It often just obscures what you're trying to do; though feel free to investigate anyway.

like image 167
Noldorin Avatar answered Sep 30 '22 02:09

Noldorin


You should favor Composition over Inheritance if the different roles have to contain different logic that would be implemented using polymorphism and abstract methods, for example:

public class User
{
    public Role Role { get; set; }
}

public abstract class Role
{
    abstract void DoRoleSpecificStuff();
}

public class Vendor : Role
{
    public void DoRoleSpecificStuff()
    {
        /* ... */
    }
}

public class Employer : Role
{
    public void DoRoleSpecificStuff()
    {
        /* ... */
    }
}

public class Advertiser : Role
{
    public void DoRoleSpecificStuff()
    {
        /* ... */
    }
}

If a User can have multiple Roles, consider using a Roles collection property:

public IEnumerable<Role> Roles { get; set; }

Otherwise, an enumeration using the [Flags] attribute could be fine, too, depending on whether you need to be able to assign multiple Roles:

public class User
{
    public Roles Roles { get; set; }
}

[Flags]
public enum Roles
{
    Advertiser = 0x0,
    Employer = 0x1,
    Vendor = 0x2      
}

You would assign a combination of different roles as follows:

User user = new User
{
    Roles = Roles.Advertiser | Roles.Vendor;
};

That would make the User both an Advertiser and a Vendor, but not an Employer.

like image 24
Marius Schulz Avatar answered Sep 30 '22 00:09

Marius Schulz


“I'm a * but I'm also a **” is known as Multiple Inheritance. C# does not support this, so you shouldn't be considering it.

like image 25
K-ballo Avatar answered Sep 30 '22 00:09

K-ballo