เป็นวิธีปฏิบัติที่ดีที่จะใช้ List of Enums หรือไม่


32

ฉันกำลังทำงานบนระบบที่มีผู้ใช้และผู้ใช้แต่ละคนมีหนึ่งหรือหลายบทบาท เป็นวิธีปฏิบัติที่ดีที่จะใช้ค่า List of Enum กับผู้ใช้หรือไม่ ฉันไม่สามารถคิดอะไรได้ดีไปกว่านี้ แต่มันก็ไม่เป็นไร

enum Role{
  Admin = 1,
  User = 2,
}

class User{
   ...
   List<Role> Roles {get;set;}
}

6
ดูดีสำหรับฉันฉันสนใจที่จะเห็นความคิดเห็นของคนอื่นในทางตรงกันข้าม
David Scholefield

9
@ Matthewthock นั่นเป็นลักษณะทั่วไปกวาดสวย รายการ <T> ค่อนข้างพบได้ทั่วไปในโลก. NET
เกรแฮม

7
@MatthewRock .NET List เป็นรายการอาร์เรย์ซึ่งมีคุณสมบัติเดียวกันสำหรับอัลกอริทึมที่คุณพูดถึง
ฉันสับสนมาก

18
@MatthewRock - ไม่คุณกำลังพูดถึงรายการ LINKED เมื่อคำถามและคนอื่น ๆ กำลังพูดถึงอินเตอร์เฟสรายการทั่วไป
Davor Ždralo

5
คุณสมบัติอัตโนมัติเป็นคุณสมบัติที่โดดเด่นของ C # (the get; set; syntax) นอกจากนี้การตั้งชื่อคลาสรายการ
jaypb

คำตอบ:


37

TL; DR: โดยปกติแล้วมันเป็นความคิดที่ดีที่จะใช้คอลเลคชัน enums เพราะมันมักจะนำไปสู่การออกแบบที่ไม่ดี คอลเลกชันของ enums มักจะเรียกร้องให้หน่วยงานของระบบที่แตกต่างกับตรรกะที่เฉพาะเจาะจง

จำเป็นต้องแยกแยะระหว่างกรณีการใช้งาน enum บางกรณี รายการนี้เป็นเพียงส่วนบนของหัวของฉันดังนั้นอาจมีกรณีเพิ่มเติม ...

ตัวอย่างมีทั้งหมดใน C # ฉันเดาว่าภาษาที่คุณเลือกจะมีโครงสร้างที่คล้ายกันหรือเป็นไปได้ที่คุณจะนำไปใช้ด้วยตนเอง

1. ค่าเดียวเท่านั้นที่ถูกต้อง

ในกรณีนี้ค่าเป็นค่าเฉพาะเช่น

public enum WorkStates
{
    Init,
    Pending,
    Done
}

มันไม่ถูกต้องให้มีการทำงานบางอย่างที่เป็นทั้งและPending Doneดังนั้นหนึ่งในค่าเหล่านี้เท่านั้นที่ถูกต้อง นี่เป็นกรณีการใช้งานที่ดีของ enum

2. การรวมกันของค่าที่ถูกต้อง
กรณีนี้เรียกว่าค่าสถานะ C # แสดง[Flags]คุณลักษณะ enum เพื่อทำงานกับสิ่งเหล่านี้ ความคิดสามารถสร้างแบบจำลองเป็นชุดของbools หรือbits กับแต่ละที่สอดคล้องกับสมาชิก 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มีDoors และWindows แต่ก็ไม่ปกติBuildingที่จะมีWheels

ในกรณีนี้จะเป็นการดีกว่าถ้าแบ่งค่า 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เป็นที่ข้อมูลเพิ่มเติมว่าเขาเป็นUserAdmin

หากคุณได้รับจะมีหลายบทบาทที่ไม่ได้พิเศษ (เช่น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 อาจเป็นวิธีที่ยอมรับได้ (เช่นประสิทธิภาพ) การตัดสินใจที่นี่ขึ้นอยู่กับข้อกำหนดของระบบแบบจำลอง


3
ฉันไม่คิดว่า[Flags]หมายถึงชุดค่าผสมทั้งหมดจะถูกต้องมากกว่าค่า int หมายความว่าค่าประเภทสี่พันล้านค่านั้นถูกต้อง มันหมายความว่าพวกเขาสามารถรวมกันในเขตข้อมูลเดียว - ข้อ จำกัด ใด ๆ ในการรวมกันเป็นตรรกะระดับสูงกว่า
Random832

คำเตือน: เมื่อใช้[Flags]งานคุณควรตั้งค่าเป็นพลังของสองมิฉะนั้นจะไม่ทำงานตามที่คาดไว้
Arturo Torres Sánchez

@ Random832 ฉันไม่เคยตั้งใจจะพูด แต่ฉันก็แก้ไขคำตอบ - หวังว่ามันชัดเจนขึ้นแล้ว
ZdeněkJelínek

1
@ ArturoTorresSánchezขอบคุณสำหรับข้อมูลที่ฉันได้แก้ไขคำตอบและยังได้เพิ่มคำอธิบายเกี่ยวกับเรื่องนี้
ZdeněkJelínek

คำอธิบายที่ดี! การตั้งค่าสถานะจริง ๆ แล้วพอดีกับความต้องการของระบบเป็นอย่างมากอย่างไรก็ตามจะง่ายกว่าที่จะใช้ HashSets เนื่องจากการใช้บริการที่มีอยู่
Dexie

79

ทำไมไม่ใช้ Set หากใช้รายการ:

  1. มันง่ายที่จะเพิ่มบทบาทเดิมสองครั้ง
  2. การเปรียบเทียบรายการอย่างไร้เดียงสาจะไม่ทำงานที่นี่อย่างถูกต้อง: [ผู้ใช้ผู้ดูแลระบบ] ไม่เหมือนกับ [ผู้ดูแลระบบผู้ใช้]
  3. การดำเนินการที่ซับซ้อนเช่นการตัดกันและการผสานจะไม่ง่ายต่อการใช้

หากคุณมีความกังวลเกี่ยวกับประสิทธิภาพการทำงานตัวอย่างเช่นใน Java มีEnumSetการใช้งานเป็นอาร์เรย์ความยาวคงที่ของบูลีน (องค์ประกอบบูลีน i'th ตอบคำถามว่าฉันมีค่า Enum อยู่ในชุดนี้หรือไม่) ตัวอย่างเช่นEnumSet<Role>. EnumMapยังดู ฉันสงสัยว่า C # มีบางอย่างที่คล้ายกัน


35
ที่จริงแล้วใน Java EnumSets ถูกนำไปใช้เป็นฟิลด์บิต
biziclop

4
คำถาม SO ที่เกี่ยวข้องHashSet<MyEnum>กับ vs. ตั้งค่าสถานะ enums: stackoverflow.com/q/9077487/87698
Heinzi

3
.NET มีFlagsAttributeซึ่งช่วยให้คุณใช้ตัวดำเนินการระดับบิตเพื่อเชื่อมต่อ enums เสียงคล้ายกับ EnumSetJava msdn.microsoft.com/en-US/LIBRARY/system.flagsattributeแก้ไข: ฉันควรดูคำตอบเดียว! มันมีตัวอย่างที่ดีของสิ่งนี้
ps2goat

4

เขียน enum ของคุณเพื่อให้คุณสามารถรวมเข้าด้วยกัน โดยการใช้เลขชี้กำลัง 2 ฐานคุณสามารถรวมพวกมันทั้งหมดใน enum เดียวได้ 1 คุณสมบัติและสามารถตรวจสอบได้ Enum ของคุณควรเป็นเช่นนี้

enum MyEnum
{ 
    FIRST_CHOICE = 2,
    SECOND_CHOICE = 4,
    THIRD_CHOICE = 8
}

3
เหตุผลใดที่คุณไม่ได้ใช้ 1
Robbie Dee

1
@RobbieDee ไม่มี effectivly ฉันสามารถใช้ 1, 2, 4, 8, ฯลฯ คุณพูดถูก
Rémi

5
ดีถ้าคุณใช้ Java ก็มีอยู่แล้วEnumSetซึ่งดำเนินการนี้enums คุณมีเลขคณิตแบบบูลทั้งหมดที่สรุปแล้วรวมถึงวิธีการอื่น ๆ เพื่อให้ใช้งานง่ายขึ้นรวมถึงหน่วยความจำขนาดเล็กและประสิทธิภาพที่ดี
fr13d

2
@ fr13d ฉันเป็น ac # guy และเนื่องจากคำถามไม่มีแท็ก java ฉันคิดว่าคำตอบนี้จะมีประโยชน์มากขึ้น
Rémi

1
ถูกต้อง@Rémi C # ให้[flags]แอตทริบิวต์ซึ่ง @ สำรวจZdeněk Jelinek ในการโพสต์ของเขา
fr13d

4

เป็นวิธีปฏิบัติที่ดีที่จะใช้ค่า List of Enum กับผู้ใช้หรือไม่


คำตอบสั้น ๆ : ใช่


คำตอบสั้น ๆ ที่ดีกว่า : ใช่ enum กำหนดบางสิ่งบางอย่างในโดเมน


คำตอบเวลาออกแบบ : สร้างและใช้คลาสโครงสร้างและอื่น ๆ ที่จำลองโดเมนในแง่ของโดเมน


คำตอบเวลาการเข้ารหัส : นี่คือวิธีการ code-Sling enums ...


คำถามที่อนุมาน :

  • ฉันควรใช้สตริงหรือไม่

    • คำตอบ: ไม่
  • ฉันควรใช้คลาสอื่น

    • นอกจากนี้ใช่ แทนที่จะไม่ใช่
  • เป็นRoleรายการ enum เพียงพอสำหรับการใช้งานในUser?

    • ฉันไม่รู้. มันขึ้นอยู่กับรายละเอียดการออกแบบอื่น ๆ ที่ไม่ได้อยู่ในหลักฐาน

บ๊อบ WTF เหรอ?

  • นี่คือคำถามออกแบบที่มีกรอบในด้านเทคนิคการเขียนโปรแกรมภาษา

    • An enumเป็นวิธีที่ดีในการกำหนด "บทบาท" ในแบบจำลองของคุณ ดังนั้นListบทบาทหนึ่งคือสิ่งที่ดี
  • enum ไกลกว่าสายอักขระ

    • Role เป็นการประกาศบทบาททั้งหมดที่มีอยู่ในโดเมน
    • สตริง "Admin" เป็นสตริงที่มีตัวอักษร"A" "d" "m" "i" "n"ตามลำดับ มันเป็นอะไรที่ไกลที่สุดเท่าที่โดเมนเป็นห่วง
  • ชั้นเรียนใด ๆ ที่คุณอาจออกแบบมาเพื่อให้แนวคิดของRoleชีวิตบางอย่าง - ฟังก์ชั่น - สามารถใช้ประโยชน์จากRoleenum

    • ตัวอย่างเช่นแทนที่จะเป็นคลาสย่อยสำหรับบทบาททุกประเภทให้มี Roleคุณสมบัติที่บอกประเภทของบทบาทคลาสอินสแตนซ์นี้
    • User.Rolesรายการเป็นที่เหมาะสมเมื่อเราไม่ต้องการหรือไม่ต้องการวัตถุบทบาท instantiated
    • และเมื่อเราต้องการสร้างอินสแตนซ์ของบทบาท enum นั้นเป็นวิธีที่ปลอดภัยและไม่ชัดเจนในการสื่อสารกับRoleFactoryชั้นเรียน และไม่สำคัญกับเครื่องอ่านรหัส

3

นั่นไม่ใช่สิ่งที่ฉันเห็น แต่ฉันไม่เห็นเหตุผลว่าทำไมมันไม่ทำงาน คุณอาจต้องการใช้รายการที่เรียงลำดับเพื่อป้องกันการดักจับอย่างไรก็ตาม

วิธีการทั่วไปคือระดับผู้ใช้เดียวเช่นระบบผู้ดูแลระบบผู้ใช้อำนาจผู้ใช้ ฯลฯ ระบบสามารถทำทุกอย่างผู้ดูแลระบบส่วนใหญ่และอื่น ๆ ลง

หากคุณต้องกำหนดค่าบทบาทเป็นพลังของทั้งสองคุณสามารถจัดเก็บบทบาทเป็นแบบ int และสืบทอดบทบาทหากคุณต้องการจัดเก็บบทบาทเหล่านั้นไว้ในสาขาเดียว แต่นั่นอาจไม่ใช่รสนิยมของทุกคน

ดังนั้นคุณอาจจะมี:

Back office role 1
Front office role 2
Warehouse role 4
Systems role 8

ดังนั้นหากผู้ใช้มีค่าบทบาทเท่ากับ 6 พวกเขาจะมีบทบาท Front office และ Warehouse


2

ใช้HashSetเพื่อป้องกันการซ้ำซ้อนและมีวิธีการเปรียบเทียบเพิ่มเติม

ใช้ Enum เป็นอย่างดี

เพิ่มเมธอด Boolean IsInRole (Role R)

และฉันจะข้ามชุด

List<Role> roles = new List<Role>();
Public List<Role> Roles { get {return roles;} }

1

ตัวอย่างง่ายๆที่คุณให้นั้นดี แต่โดยทั่วไปแล้วมันซับซ้อนกว่านั้น ตัวอย่างเช่นหากคุณต้องการให้ผู้ใช้และบทบาทในฐานข้อมูลคงอยู่คุณควรกำหนดบทบาทในตารางฐานข้อมูลแทนที่จะใช้ enums ที่ให้คุณอ้างอิงความสมบูรณ์


0

หากระบบ - อาจเป็นซอฟต์แวร์ระบบปฏิบัติการหรือองค์กร - เกี่ยวข้องกับบทบาทและการอนุญาตมีเวลาที่จะมีประโยชน์ในการเพิ่มบทบาทและจัดการสิทธิ์สำหรับพวกเขา
หากสามารถเก็บถาวรได้โดยการแก้ไขซอร์สโค้ดมันอาจจะโอเคที่จะติดกับ enums แต่ในบางจุดผู้ใช้ระบบต้องการที่จะสามารถจัดการบทบาทและสิทธิ์ได้ กว่า Enums กลายเป็นใช้ไม่ได้
แต่คุณจะต้องมีโครงสร้างข้อมูลที่กำหนดค่าได้ เช่นคลาส UserRole ที่มีชุดของสิทธิ์ที่กำหนดให้กับบทบาท
คลาสผู้ใช้จะมีชุดของบทบาท หากไม่จำเป็นต้องตรวจสอบการอนุญาตของผู้ใช้ชุดสหภาพของสิทธิ์บทบาททั้งหมดจะถูกสร้างขึ้นและตรวจสอบว่ามีการอนุญาตในคำถาม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.