我目前正在一个有用户的系统上工作,每个用户都有一个或多个角色。在User上使用Enum值列表是一种好习惯吗?我想不出更好的办法,但这感觉还不错。
enum Role{
Admin = 1,
User = 2,
}
class User{
...
List<Role> Roles {get;set;}
}
我目前正在一个有用户的系统上工作,每个用户都有一个或多个角色。在User上使用Enum值列表是一种好习惯吗?我想不出更好的办法,但这感觉还不错。
enum Role{
Admin = 1,
User = 2,
}
class User{
...
List<Role> Roles {get;set;}
}
Answers:
TL; DR:使用枚举集合通常是一个坏主意,因为它通常会导致不良的设计。枚举的集合通常要求具有特定逻辑的不同系统实体。
有必要区分一些枚举用例。这个清单只是我的头等大事,所以可能会有更多的情况...
这些示例全部使用C#编写,我想您选择的语言将具有类似的构造,或者您可以自己实现。
1.仅单个值有效
在这种情况下,这些值是互斥的,例如
public enum WorkStates
{
Init,
Pending,
Done
}
它是无效的一些工作,既Pending
和Done
。因此,这些值中只有一个有效。这是枚举的一个很好的用例。
2.值的组合是有效的
这种情况也称为标志,C#提供了[Flags]
枚举属性来处理这些标志。可以将该想法建模为一组bool
或,bit
其中每个对应一个枚举成员。每个成员的乘方数值应为2。可以使用按位运算符创建组合:
[Flags]
public enum Flags
{
None = 0,
Flag0 = 1, // 0x01, 1 << 0
Flag1 = 2, // 0x02, 1 << 1
Flag2 = 4, // 0x04, 1 << 2
Flag3 = 8, // 0x08, 1 << 3
Flag4 = 16, // 0x10, 1 << 4
AFrequentlyUsedMask = Flag1 | Flag2 | Flag4,
All = ~0 // bitwise negation of zero is all ones
}
在每个枚举成员仅代表一个已设置或未设置的位的情况下,使用枚举成员集合是一个过大的杀伤力。我猜大多数语言都支持这样的构造。否则,您可以创建一个(例如,使用bool[]
并使用寻址(1 << (int)YourEnum.SomeMember) - 1
)。
a)所有组合均有效
尽管在某些简单情况下可以使用这些对象,但是对象集合可能更合适,因为您通常需要基于类型的其他信息或行为。
[Flags]
public enum Flavors
{
Strawberry = 1,
Vanilla = 2,
Chocolate = 4
}
public class IceCream
{
private Flavors _scoopFlavors;
public IceCream(Flavors scoopFlavors)
{
_scoopFlavors = scoopFlavors
}
public bool HasFlavor(Flavors flavor)
{
return _scoopFlavors.HasFlag(flavor);
}
}
(注意:这是假设您只真正关心冰淇淋的口味-不需要将冰淇淋建模为勺子和圆锥的集合)
b)某些值组合是有效的,而某些则无效
这是一个常见的情况。通常情况是,您将两个不同的东西放到一个枚举中。例:
[Flags]
public enum Parts
{
Wheel = 1,
Window = 2,
Door = 4,
}
public class Building
{
public Parts parts { get; set; }
}
public class Vehicle
{
public Parts parts { get; set; }
}
现在,虽然这是完全有效的两种Vehicle
,并Building
有Door
S和Window
S,它不是很平常的Building
s到有Wheel
秒。
在这种情况下,最好将枚举分解为多个部分和/或修改对象层次结构,以实现情况#1或#2a)。
设计注意事项
不知何故,枚举往往不是OO中的驱动因素,因为可以将实体的类型视为类似于枚举通常提供的信息。
以IceCream
#2中的样本为例,该IceCream
实体将代替标志而具有Scoop
对象的集合。
较不纯粹的方法是Scoop
拥有Flavor
财产。最纯粹的做法是为Scoop
成为一个抽象基类VanillaScoop
,ChocolateScoop
...类来代替。
底线是:
1.并非所有“某种事物的类型”都需要枚举
2.当某些枚举成员在某些情况下不是有效的标志时,请考虑将枚举拆分为多个不同的枚举。
现在为您的示例(稍作更改):
public enum Role
{
User,
Admin
}
public class User
{
public List<Role> Roles { get; set; }
}
我认为这种确切的情况应建模为(注意:并非真正可扩展!):
public class User
{
public bool IsAdmin { get; set; }
}
换句话说-这是隐含的,User
是是User
,另外的信息是他是否是一个Admin
。
如果你有这不是唯一的多重角色(例如User
可以是Admin
,Moderator
,VIP
,...在同一时间),这将是一个很好用的时间无论是标志枚举或abtract基类或接口。
使用类来表示Role
线索可以更好地划分职责,其中a Role
可以负责决定是否可以执行给定的操作。
使用枚举,您需要将所有角色的逻辑放在一个地方。这违背了OO的目的,并使您回到当务之急。
假设a Moderator
具有编辑权限,Admin
并且同时具有编辑和删除权限。
枚举方法(Permissions
为了不混合角色和权限而调用):
[Flags]
public enum Permissions
{
None = 0
CanEdit = 1,
CanDelete = 2,
ModeratorPermissions = CanEdit,
AdminPermissions = ModeratorPermissions | CanDelete
}
public class User
{
private Permissions _permissions;
public bool CanExecute(IAction action)
{
if (action.Type == ActionType.Edit && _permissions.HasFlag(Permissions.CanEdit))
{
return true;
}
if (action.Type == ActionType.Delete && _permissions.HasFlag(Permissions.CanDelete))
{
return true;
}
return false;
}
}
类方法(这远非完美,理想情况下,您希望IAction
以访客模式使用,但本文已经非常多了……):
public interface IRole
{
bool CanExecute(IAction action);
}
public class ModeratorRole : IRole
{
public virtual bool CanExecute(IAction action)
{
return action.Type == ActionType.Edit;
}
}
public class AdminRole : ModeratorRole
{
public override bool CanExecute(IAction action)
{
return base.CanExecute(action) || action.Type == ActionType.Delete;
}
}
public class User
{
private List<IRole> _roles;
public bool CanExecute(IAction action)
{
_roles.Any(x => x.CanExecute(action));
}
}
但是,使用枚举可能是可以接受的方法(例如,性能)。这里的决定取决于建模系统的要求。
[Flags]
意味着所有组合都是有效的,而int意味着该类型的所有四十亿个值都是有效的。这只是意味着它们可以在单个字段中组合-组合的任何限制都属于更高级别的逻辑。
[Flags]
,应将值设置为2的幂,否则将无法正常工作。
为什么不使用Set?如果使用列表:
如果您担心性能,那么例如在Java中,可以将EnumSet
其实现为固定长度的布尔数组(第i个布尔元素回答是否在该集合中存在第i个Enum值的问题)。例如,EnumSet<Role>
。另请参阅EnumMap
。我怀疑C#有类似的东西。
EnumSet
它们是作为位字段实现的。
FlagsAttribute
,它使您可以使用按位运算符来连接枚举。听起来类似于Java EnumSet
。msdn.microsoft.com/zh-CN/LIBRARY/system.flagsattribute编辑:我应该看不起一个答案!它有一个很好的例子。
编写您的枚举,以便可以将它们合并。通过使用以2为底的指数,您可以将它们全部合并为一个枚举,具有1个属性并可以进行检查。你的枚举应该是这样
enum MyEnum
{
FIRST_CHOICE = 2,
SECOND_CHOICE = 4,
THIRD_CHOICE = 8
}
EnumSet
为enum
s 实现了此功能。您已经提取了所有布尔运算,还添加了一些使其更易于使用的方法,以及较小的内存和良好的性能。
在User上使用Enum值列表是一种好习惯吗?
简短答案:是
更好的简短答案:是的,枚举定义了域中的某些内容。
设计时答案:制作和使用根据领域本身对领域进行建模的类,结构等。
编码时间答案:这是枚举枚举代码的方法...
推断的问题:
我应该使用琴弦吗?
我应该使用其他班级吗?
是Role
枚举列表足够使用User
?
WTF鲍勃?
这是编程语言技术中的设计问题。
enum
是在模型中定义“角色”的好方法。因此List
,角色扮演是一件好事。enum
远胜于弦乐。
Role
是域中存在的所有角色的声明。 "A"
"d"
"m"
"i"
"n"
顺序排列的字符串。它是什么,只要域而言。您可以设计用来赋予Role
生命概念(功能)的任何类都可以充分利用Role
枚举。
Role
属性来指示此类实例是哪种角色。User.Roles
列表是合理的,当我们不需要或不想要的,实例化的角色对象。RoleFactory
类。并且对于代码阅读器而言也并非无关紧要。如果系统(可能是软件,操作系统或组织)处理角色和权限,那么有时候添加角色和管理角色很有用。
如果可以通过更改源代码将其归档,则可以坚持枚举。但是在某个时候,系统用户希望能够管理角色和权限。枚举变得无法使用。
相反,您将需要具有可配置的数据结构。即UserRole类,该类还拥有分配给角色的一组权限。
User类将具有一组角色。如果需要检查用户的权限,则会创建所有角色权限的并集,并检查其中是否包含所涉及的权限。