TL; DR: โดยปกติแล้วมันเป็นความคิดที่ดีที่จะใช้คอลเลคชัน enums เพราะมันมักจะนำไปสู่การออกแบบที่ไม่ดี คอลเลกชันของ enums มักจะเรียกร้องให้หน่วยงานของระบบที่แตกต่างกับตรรกะที่เฉพาะเจาะจง
จำเป็นต้องแยกแยะระหว่างกรณีการใช้งาน enum บางกรณี รายการนี้เป็นเพียงส่วนบนของหัวของฉันดังนั้นอาจมีกรณีเพิ่มเติม ...
ตัวอย่างมีทั้งหมดใน C # ฉันเดาว่าภาษาที่คุณเลือกจะมีโครงสร้างที่คล้ายกันหรือเป็นไปได้ที่คุณจะนำไปใช้ด้วยตนเอง
1. ค่าเดียวเท่านั้นที่ถูกต้อง
ในกรณีนี้ค่าเป็นค่าเฉพาะเช่น
public enum WorkStates
{
Init,
Pending,
Done
}
มันไม่ถูกต้องให้มีการทำงานบางอย่างที่เป็นทั้งและPending
Done
ดังนั้นหนึ่งในค่าเหล่านี้เท่านั้นที่ถูกต้อง นี่เป็นกรณีการใช้งานที่ดีของ enum
2. การรวมกันของค่าที่ถูกต้อง
กรณีนี้เรียกว่าค่าสถานะ C # แสดง[Flags]
คุณลักษณะ enum เพื่อทำงานกับสิ่งเหล่านี้ ความคิดสามารถสร้างแบบจำลองเป็นชุดของbool
s หรือbit
s กับแต่ละที่สอดคล้องกับสมาชิก enum หนึ่ง สมาชิกแต่ละคนควรมีค่าพลังสอง ชุดค่าผสมสามารถสร้างขึ้นโดยใช้ตัวดำเนินการระดับบิต:
[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
}
การใช้คอลเลกชันของสมาชิก enum เป็น overkill ในกรณีเช่นนี้เนื่องจากสมาชิก enum แต่ละคนจะแสดงเพียงหนึ่งบิตที่ถูกตั้งค่าหรือยกเลิกการตั้งค่า ฉันเดาว่าภาษาส่วนใหญ่รองรับโครงสร้างเช่นนี้ มิฉะนั้นคุณสามารถสร้างขึ้นมาใหม่ (เช่นใช้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) การรวมกันของค่าบางอย่างที่ถูกต้องและบางส่วนไม่
นี่เป็นสถานการณ์ที่เกิดขึ้นบ่อยครั้ง กรณีนี้อาจเกิดจากการที่คุณใส่สองสิ่งที่แตกต่างกันลงไปใน Enum เดียว ตัวอย่าง:
[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
ที่จะมีWheel
s
ในกรณีนี้จะเป็นการดีกว่าถ้าแบ่งค่า enum ออกเป็นส่วนต่างๆและ / หรือแก้ไขลำดับชั้นวัตถุเพื่อให้ได้ขนาด # 1 หรือ # 2a ทั้งสองกรณี
ข้อควรพิจารณาในการออกแบบ
อย่างไรก็ตาม enums ไม่น่าจะเป็นองค์ประกอบการขับขี่ใน OO เนื่องจากประเภทของเอนทิตีสามารถพิจารณาได้คล้ายกับข้อมูลที่ enum ให้มา
ใช้เวลาIceCream
ตัวอย่างจาก # 2 IceCream
เอนทิตีจะแทนธงมีชุดของScoop
วัตถุ
วิธีการที่พิถีพิถันน้อยกว่านั้นก็คือการที่Scoop
จะมีFlavor
ทรัพย์สิน วิธีการจะเป็นคนเจ้าระเบียบสำหรับScoop
ที่จะเป็นชั้นฐานนามธรรมVanillaScoop
, ChocolateScoop
... ชั้นเรียนแทน
บรรทัดล่างคือ:
1. ไม่ใช่ทุกอย่างที่เป็น "ประเภทของบางสิ่งบางอย่าง" ต้องเป็น enum
2 เมื่อสมาชิก enum บางคนไม่ได้ตั้งค่าสถานะที่ถูกต้องในบางสถานการณ์ให้พิจารณาแบ่ง enum ออกเป็นหลายกลุ่ม
ตอนนี้สำหรับตัวอย่างของคุณ (เปลี่ยนแปลงเล็กน้อย):
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
... ในเวลาเดียวกัน) ซึ่งจะเป็นเวลาที่ดีที่จะใช้อย่างใดอย่างหนึ่งธง enum หรือชั้นฐาน abtract หรืออินเตอร์เฟซ
การใช้คลาสเพื่อเป็นRole
ผู้นำในการแยกความรับผิดชอบที่ดีขึ้นซึ่ง a Role
สามารถมีความรับผิดชอบในการตัดสินใจว่าจะสามารถดำเนินการตามที่กำหนดได้หรือไม่
ด้วย enum คุณจะต้องมีเหตุผลในที่เดียวสำหรับบทบาททั้งหมด ซึ่งเอาชนะวัตถุประสงค์ของ OO และนำคุณกลับสู่ความจำเป็น
ลองนึกภาพว่า a Moderator
มีสิทธิ์แก้ไขและAdmin
มีทั้งแก้ไขและลบสิทธิ์
วิธีการ Enum (เรียกว่า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));
}
}
การใช้ enum อาจเป็นวิธีที่ยอมรับได้ (เช่นประสิทธิภาพ) การตัดสินใจที่นี่ขึ้นอยู่กับข้อกำหนดของระบบแบบจำลอง