TL; DR: Thường là một ý tưởng tồi khi sử dụng một bộ sưu tập các enum vì nó thường dẫn đến một thiết kế xấu. Một tập hợp các enum thường gọi các thực thể hệ thống riêng biệt với logic cụ thể.
Cần phân biệt giữa một vài trường hợp sử dụng enum. Danh sách này chỉ đứng đầu tôi nên có thể có nhiều trường hợp hơn ...
Các ví dụ đều có trong C #, tôi đoán ngôn ngữ bạn chọn sẽ có cấu trúc tương tự hoặc bạn có thể tự thực hiện chúng.
1. Chỉ một giá trị duy nhất là hợp lệ
Trong trường hợp này, các giá trị là độc quyền, vd
public enum WorkStates
{
Init,
Pending,
Done
}
Nó là không hợp lệ để có một số công việc là cả hai Pending
và Done
. Do đó, chỉ một trong những giá trị này là hợp lệ. Đây là một trường hợp sử dụng tốt của enum.
2. Kết hợp các giá trị là hợp lệ
Trường hợp này còn được gọi là cờ, C # cung cấp [Flags]
thuộc tính enum để làm việc với các giá trị này. Ý tưởng có thể được mô hình thành một tập hợp bool
s hoặcbit
s với mỗi tương ứng với một thành viên enum. Mỗi thành viên nên có một giá trị sức mạnh của hai. Kết hợp có thể được tạo bằng các toán tử bitwise:
[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
}
Sử dụng một tập hợp các thành viên enum là một việc quá mức trong trường hợp như vậy vì mỗi thành viên enum chỉ đại diện cho một bit được đặt hoặc không được đặt. Tôi đoán hầu hết các ngôn ngữ hỗ trợ các cấu trúc như thế này. Nếu không, bạn có thể tạo một (ví dụ: sử dụngbool[]
và giải quyết nó bằng cách (1 << (int)YourEnum.SomeMember) - 1
).
a) Tất cả các kết hợp là hợp lệ
Mặc dù những điều này là ổn trong một số trường hợp đơn giản, một bộ sưu tập các đối tượng có thể phù hợp hơn vì bạn thường cần thêm thông tin hoặc hành vi dựa trên loại.
[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);
}
}
(lưu ý: điều này giả sử bạn chỉ thực sự quan tâm đến hương vị của kem - rằng bạn không cần phải mô hình hóa kem như một bộ sưu tập muỗng và hình nón)
b) Một số kết hợp các giá trị là hợp lệ và một số không
Đây là một kịch bản thường xuyên. Trường hợp thường có thể là bạn đặt hai thứ khác nhau vào một enum. Thí dụ:
[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; }
}
Bây giờ trong khi nó hoàn toàn hợp lệ cho cả hai Vehicle
và Building
có Door
s và Window
s, nó không phải là rất bình thường đối với Building
s có Wheel
s.
Trong trường hợp này, tốt hơn là chia enum thành các phần và / hoặc sửa đổi hệ thống phân cấp đối tượng để đạt được trường hợp # 1 hoặc # 2a).
Thiết kế cân nhắc
Bằng cách nào đó, enum có xu hướng không phải là yếu tố thúc đẩy trong OO vì loại thực thể có thể được coi là tương tự như thông tin thường được cung cấp bởi một enum.
Lấy ví dụ IceCream
mẫu từ # 2,IceCream
thực thể thay vì cờ có một bộ sưu tập các Scoop
đối tượng.
Cách tiếp cận ít thuần túy hơn sẽ là Scoop
để có một Flavor
tài sản. Cách tiếp cận thuần túy sẽ Scoop
là một lớp cơ sở trừu tượng cho VanillaScoop
,ChocolateScoop
, ... các lớp học để thay thế.
Điểm mấu chốt là:
1. Không phải tất cả mọi thứ là "loại của một cái gì đó" cần phải được enum
2. Khi một số thành viên enum không phải là cờ hợp lệ trong một số trường hợp, hãy xem xét chia enum thành nhiều enum khác nhau.
Bây giờ cho ví dụ của bạn (thay đổi một chút):
public enum Role
{
User,
Admin
}
public class User
{
public List<Role> Roles { get; set; }
}
Tôi nghĩ trường hợp chính xác này nên được mô hình hóa thành (lưu ý: không thực sự mở rộng!):
public class User
{
public bool IsAdmin { get; set; }
}
Nói cách khác - đó là ẩn ý, rằng đó User
là một User
, thông tin bổ sung là liệu anh ta có phải là mộtAdmin
.
Nếu bạn có thể có nhiều vai trò mà không phải là độc quyền (ví dụ như User
có thể Admin
, Moderator
, VIP
, ... cùng một lúc), đó sẽ là một thời điểm tốt để sử dụng một trong hai lá cờ enum hoặc một lớp cơ sở TÓM TẮT hoặc giao diện.
Sử dụng một lớp để thể hiện một Role
khách hàng tiềm năng để phân tách trách nhiệm tốt hơn trong đó một người Role
có thể có trách nhiệm quyết định liệu họ có thể thực hiện một hành động nhất định hay không.
Với một enum, bạn cần có logic ở một nơi cho tất cả các vai trò. Mà đánh bại mục đích của OO và đưa bạn trở lại mệnh lệnh.
Hãy tưởng tượng rằng a Moderator
có quyền chỉnh sửa và Admin
có cả quyền chỉnh sửa và xóa.
Cách tiếp cận Enum (được gọi Permissions
để không trộn lẫn vai trò và quyền):
[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;
}
}
Cách tiếp cận lớp học (điều này không hoàn hảo, lý tưởng nhất là bạn muốn IAction
mô hình khách truy cập nhưng bài đăng này đã rất lớn ...):
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));
}
}
Sử dụng một enum có thể là một cách tiếp cận chấp nhận được (ví dụ: hiệu suất). Quyết định ở đây phụ thuộc vào yêu cầu của hệ thống mô hình.