可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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.
回答1:
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.
回答2:
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.
回答3:
“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.
回答4:
It's indeed composition over inheritance here, but it's more like this if a single user can have multiple roles.
If there are relatively few roles, a 'parking lot' analogous to an outer join result may work. In this pattern, no Role
base class is required.
class User
{
// all of these may be null if not applicable
VendorRole VendorRole { get; set; }
EmployeeRole EmployeeRole { get; set; }
AdvertiserRole AdvertiserRole { get; set; }
}
If a user may have multiple instances of a single role, a collection pops up:
class User
{
// all of these may be null if not applicable
VendorRole VendorRole { get; set; }
EmployeeRole EmployeeRole { get; set; }
ICollection<AdvertiserRole> AdvertiserRoles { get; }
}
Alternatively, if there may be a messy pile of roles, if roles get added dynamically, or what have you, you'll need a collection and a base type. If Entity Framework is involved, though, dynamically added roles seem unlikely to me.
class User
{
ICollection<Role> Roles;
}