Enums Listesi'ni kullanmak iyi bir uygulama mıdır?


32

Şu anda Kullanıcıların bulunduğu bir sistem üzerinde çalışıyorum ve her kullanıcının bir veya daha fazla rolü var. Kullanıcıda Enum değerleri listesini kullanmak iyi bir uygulama mıdır? Daha iyi bir şey düşünemiyorum, ama bu pek iyi değil.

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

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

6
Bana iyi gözüküyor, tersine başkalarının yorumlarını görmek isterim.
David Scholefield,

9
@MatthewRock, oldukça kapsamlı bir genelleme. .NET dünyasında <T> listesi oldukça yaygındır.
Graham

7
@MatthewRock .NET List, bahsettiğiniz algoritmalar için aynı özelliklere sahip olan bir arraylist'tir.
çok karıştı

18
@MatthewRock - hayır, LINKED listelerinden bahsediyorsunuz, soru ve diğerleri genel Liste arayüzünden bahsediyor.
Davor Ždralo,

5
Otomatik özellikler C # 'nın ayırt edici bir özelliğidir (get; set; sentaks). Ayrıca List sınıfının isimlendirilmesi.
jaypb

Yanıtlar:


37

TL; DR: Genellikle kötü bir tasarıma yol açtığından, bir toplama koleksiyonu kullanmak genellikle kötü bir fikirdir. Enums koleksiyonu genellikle belirli mantıklara sahip farklı sistem varlıklarını gerektirir.

Birkaç kullanım enum durumu arasında ayrım yapmak gerekir. Bu liste sadece kafamın en üstünde, bu yüzden daha fazla dava olabilir ...

Örneklerin hepsi C # 'da, sanırım tercih ettiğiniz dil benzer yapılara sahip olacak veya bunları kendiniz uygulamanız mümkün olacak.

1. Sadece tek değer geçerlidir

Bu durumda, değerler münhasırdır;

public enum WorkStates
{
    Init,
    Pending,
    Done
}

Hem bazı iş var geçersiz Pendingve Done. Bu nedenle bu değerlerden sadece biri geçerlidir. Bu iyi bir enum örneğidir.

2. Değerlerin bir kombinasyonu geçerlidir.
Bu duruma bayraklar da denir, C # [Flags]bunlarla çalışmak için enum niteliği sağlar . Fikir, her biri bir enum üyesine karşılık gelen bir bools veya bits olarak modellenebilir . Her üyenin iki gücüne sahip olması gerekir. Bitsel operatörler kullanılarak kombinasyonlar oluşturulabilir:

[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 üyeleri koleksiyonunun kullanılması, her enum üyesi yalnızca ayarlanmış veya ayarlanmamış bir biti temsil ettiği için böyle bir durumda geçersizdir. Sanırım çoğu dil bunun gibi yapıları destekliyor. Aksi takdirde bir tane oluşturabilirsiniz (örneğin, kullanıp bool[]adresleyin (1 << (int)YourEnum.SomeMember) - 1).

a) Tüm kombinasyonlar geçerlidir

Bazı basit durumlarda bunlar uygun olsa da, genellikle türüne bağlı olarak ek bilgi veya davranışa ihtiyaç duyduğunuz için bir nesne koleksiyonu daha uygun olabilir.

[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);
    }
}

(not: bu, yalnızca dondurmanın lezzetlerini gerçekten umursadığınızı varsayar; dondurmayı kepçe ve bir külah koleksiyonu olarak modellemenize gerek yoktur)

b) Bazı değer kombinasyonları geçerlidir, bazıları değil

Bu sık sık bir senaryodur. Durum genellikle, iki farklı şeyi bir numaraya koymanız olabilir. Örnek:

[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; }
}

O ikisi için tamamen geçerli iken Şimdi Vehicleve Buildingsahip olmak Doors ve Windows için, çok olağan değil Buildingler var Wheels.

Bu durumda, enumumu parçalara ayırmak ve / veya her iki durumda da # 1 veya # 2a) elde etmek için nesne hiyerarşisini değiştirmek daha iyi olacaktır.

Tasarım konuları

Her nasılsa, uçlar OO'daki itici elemanlar olma eğilimindedir, çünkü varlık tipi genellikle enum tarafından sağlanan bilgilere benzer olarak değerlendirilebilir.

IceCreamÖrnek 2 # 'den bir örnek alın IceCream, bayraklar yerine varlıkların bir Scoopnesne koleksiyonu olur .

İçin daha az pürist yaklaşım olur Scoopbir olması Flavorözelliğini. İçin pürist yaklaşım olacağını Scoopiçin soyut bir taban sınıfı olmak VanillaScoop, ChocolateScoopbunun yerine, ... sınıflar.

Alt satırda:
1. "Bir türden bir şey" olan her şeyin
enum olması gerekmiyor. 2. Bazı enum üyeleri bazı senaryolarda geçerli bir bayrak değilse, enumu birden farklı enumlara bölmeyi düşünün.

Şimdi, örneğin (biraz değişmiş):

public enum Role
{
    User,
    Admin
}

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

Bu tam davanın şu şekilde modellenmesi gerektiğini düşünüyorum (not: gerçekten genişletilemez!):

public class User
{
    public bool IsAdmin { get; set; }
}

Başka bir deyişle - zımni olduğu, yani Usera User, ek bilgi onun bir olup olmadığıdır Admin.

Eğer özel değildir birden çok rol var alırsanız (örneğin Userolabilir Admin, Moderator, VIP, ... aynı zamanda), bu da bayraklar enum kullanmak için iyi bir zaman ya da bir abtract taban sınıfı veya arabirim olurdu.

Bir sınıfı temsil etmek için bir sınıf kullanmak, verilen bir işlemi yapıp yapamayacağına karar verme sorumluluğunun olabileceği Roledurumlarda sorumlulukların daha iyi ayrılmasına yol açar Role.

Enum ile, tüm roller için mantığı tek bir yerde bulundurmanız gerekir. OO'nun amacını yitirir ve sizi zorunlu hale getirir.

Bir Moderatordüzenleme haklarına sahip olduğunu ve Adminhem düzenleme hem de silme haklarına sahip olduğunu hayal edin .

Enum yaklaşımı ( Permissionsrolleri ve izinleri karıştırmamak için çağrılır ):

[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;
    }
}

Sınıf yaklaşımı (bu mükemmel olmaktan uzak, ideal olarak, IActionbir ziyaretçi düzeninde yer almak istersiniz, ancak bu yazı zaten çok büyük ...):

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));
    }
}

Bir enum kullanmak olsa da (örneğin performans) kabul edilebilir bir yaklaşım olabilir. Buradaki karar, modellenen sistemin gereksinimlerine bağlıdır.


3
[Flags]Tüm kombinasyonların geçerli olduğunu ima etmenin bir int'den daha fazla geçerli olduğunu sanmıyorum , bu türün dört milyar değerinin hepsi geçerli. Bu sadece onlar anlamı olabilir tek bir alanı içinde birleştirilebilir - kombinasyonları üzerinde herhangi bir kısıtlama üst düzey mantığı aittir.
Random832

Uyarı: kullanırken [Flags], değerleri ikiden oluşan güçlere ayarlamanız gerekir, aksi takdirde beklendiği gibi çalışmaz.
Arturo Torres Sánchez,

@ Random832 Bunu söylemeye hiç niyetim olmadı ama cevabı değiştirdim - umarım şimdi daha açıktır.
Zdeněk Jelínek

1
@ ArturoTorresSánchez Girişiniz için teşekkür ederim, cevabı düzelttim ve bunun hakkında da açıklayıcı bir not ekledim.
Zdeněk Jelínek

Harika bir açıklama! Aslında bayraklar sistem gereksinimlerini çok iyi karşılar, ancak mevcut hizmet uygulaması nedeniyle HashSets'i kullanmak daha kolay olacaktır.
Dexie,

79

Neden Set'i kullanmıyorsun? Liste kullanıyorsanız:

  1. Aynı rolü iki kere eklemek kolaydır
  2. Naif bir liste karşılaştırması burada düzgün çalışmıyor: [Kullanıcı, Yönetici] [Yönetici, Kullanıcı] ile aynı değil
  3. Kesişme ve birleştirme gibi karmaşık işlemlerin uygulanması kolay değildir

Performanstan endişe duyuyorsanız, o zaman, örneğin Java'da, EnumSetsabit uzunluktaki boolean dizisi olarak uygulanır (boolean elementi, Enum değerinin bu sette mevcut olup olmadığı sorusuna cevap verir). Örneğin, EnumSet<Role>. Ayrıca bakınız EnumMap. C # 'nin benzer bir şey olduğundan şüpheleniyorum.


35
Aslında Java’larda EnumSetbit alanları olarak uygulanır.
biziclop

4
HashSet<MyEnum>Bayraklar vs ile ilgili SO sorusu : stackoverflow.com/q/9077487/87698
Heinzi,

3
.NET, FlagsAttributebitleri işlemek için bitsel operatörleri kullanmanızı sağlar. Java'ya benziyor EnumSet. msdn.microsoft.com/en-US/LIBRARY/system.flagsattribute EDIT: Bir cevap vermeliydim! bunun iyi bir örneği var.
ps2goat,

4

Enumunuzu birleştirerek onları birleştirebilirsiniz. Üs 2 üstel kullanarak hepsini bir numarada birleştirebilir, 1 özelliğe sahip olabilir ve bunları kontrol edebilirsiniz. Enum bu kadar olmalı

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

3
1'i kullanmamanın bir nedeni var mı?
Robbie Dee

1
@RobbieEtkili olarak hayır, 1, 2, 4, 8, vb. Kullanabilirdim. Haklısın
Rémi

5
Eğer Java kullanıyorsanız, bunu s EnumSetiçin uygulayan bir program zaten var enum. Boolean aritmetiğinin zaten soyutlanmış hali, ayrıca kullanımı kolaylaştırmak için başka yöntemler, küçük bellek ve iyi performans var.
fr13d,

2
@ fr13d Ben ac # adamım ve soru java etiketi olmadığı için bu cevabın daha çok uygulandığını düşünüyorum
Rémi

1
Doğru, @ Rémi. C #, [flags]@ Zdeněk Jelínek'in görevinde araştırdığı bir özellik sağlar .
fr13d,

4

Kullanıcıda Enum değerleri listesini kullanmak iyi bir uygulama mıdır?


Kısa cevap : Evet


Daha iyi kısa cevap : Evet, enum, etki alanında bir şey tanımlar.


Tasarım zamanı yanıtı : Etki alanını etki alanı açısından modelleyen sınıflar, yapılar vb. Kullanın ve kullanın.


Kodlama zamanı cevabı : İşte kodlama kodları nasıl?


Çıkarılan sorular :

  • Dizeleri kullanmalı mıyım?

    • cevap: Hayır.
  • Başka bir sınıf mı kullanmalıyım?

    • Ek olarak, evet. Hayır, yerine.
  • Roleenum liste halinde kullanım için yeterli User?

    • Hiç bir fikrim yok. Kanıtta olmayan diğer tasarım detaylarına oldukça bağlıdır.

WTF Bob?

  • Bu programlama dili tekniklerinde çerçeveli bir tasarım sorusudur.

    • Bir enummodelinizde "Roller" tanımlamak için iyi bir yoldur. O zaman Listrollerden biri iyi bir şeydir.
  • enum dizeleri çok daha üstün.

    • Role etki alanında mevcut olan TÜM rollerin bildirimidir.
    • "Yönetici" dizesi, bu sırada harfleri olan bir dizedir "A" "d" "m" "i" "n". Öyle bir şey kadarıyla alanı söz konusu olduğunda.
  • RoleYaşam kavramı vermek için tasarlayabileceğiniz herhangi bir sınıf - işlevsellik - Roleenumdan faydalanabilir .

    • Örneğin, her tür rol için alt sınıf yerine, Rolebu sınıf örneğinin ne tür bir rol olduğunu söyleyen bir özelliği vardır.
    • Bir örnek User.Roles, somutlaştırılmış rol nesnelerine ihtiyacımız olmadığında veya istemediğimizde makul olur.
    • Ve bir rolü canlandırmamız gerektiğinde, enum belirsiz, güvenli bir RoleFactorysınıftır; ve, önemsiz bir şekilde, kod okuyucusuna.

3

Bu gördüğüm bir şey değil ama işe yaramayacağına dair hiçbir neden göremiyorum. Bununla birlikte, sıralı listeyi kullanmak isteyebilirsiniz, böylece yinelemelere karşı korunur.

Daha yaygın bir yaklaşım, tek bir kullanıcı seviyesidir; örneğin, sistem, yönetici, uzman kullanıcı, kullanıcı vb. Sistem her şeyi yapabilir, çoğu şeyi yönetebilir.

Rollerin değerlerini ikisinin gücü olarak ayarlayacak olsaydınız, rolü bir int olarak saklayabilir ve gerçekten tek bir alanda saklamak istese de rolleri çıkarabilirsiniz, ancak bu herkesin zevkine uygun olmayabilir.

Yani olabilir:

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

Yani bir kullanıcı 6'lık bir role sahip olsaydı, Ön büro ve Depo rolleri olurdu.


2

Yinelemeleri engellediğinden ve daha fazla karşılaştırma yöntemi olduğundan HashSet'i kullanın

Aksi takdirde Enum iyi kullanmak

Bir yöntem ekle Boolean IsInRole (Rol R)

ve seti atlardım

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

1

Verdiğiniz basit örnek iyi, ama genellikle ondan daha karmaşık. Örneğin, bir veritabanındaki kullanıcıları ve rolleri devam ettirecekseniz, rolleri enums kullanmak yerine veritabanı tablosunda tanımlamanız gerekir. Bu size referans bütünlüğü verir.


0

Bir sistem - bir yazılım, bir işletim sistemi veya bir kuruluş olabilirse - roller ve izinlerle ilgileniyorsa, roller eklemenin ve bunlar için izinleri yönetmenin yararlı olduğu bir zaman gelir.
Bu, kaynak kodunu değiştirerek arşivlenebilirse, enums'lere bağlı kalmak uygun olabilir. Ancak bir noktada sistemin kullanıcısı rolleri ve izinleri yönetebilmek istiyor. Enums kullanılamaz hale gelir.
Bunun yerine, yapılandırılabilir bir veri yapısına sahip olmak isteyeceksiniz. yani, role atanmış bir izin kümesine sahip olan bir UserRole sınıfı.
Bir Kullanıcı sınıfı bir takım rollere sahip olacaktır. Kullanıcının iznini kontrol etme ihtiyacı varsa, tüm rol izinlerinden oluşan bir sendika seti oluşturulur ve söz konusu izni içerip içermediğini kontrol eder.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.