List of Enumsを使用するのは良い習慣ですか?


32

現在、ユーザーが存在し、各ユーザーが1つまたは複数のロールを持つシステムで作業しています。ユーザーに列挙値のリストを使用することは良い習慣ですか?これ以上良いものは考えられませんが、これは大丈夫ではありません。

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

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

6
私には見た目が良いのですが、逆に他の人のコメントを見てみたいです。
デビッドショールフィールド

9
@MatthewRockはかなり抜本的な一般化です。List <T>は、.NETの世界では非常に一般的です。
グラハム

7
@MatthewRock .NET Listは配列リストであり、言及したアルゴリズムと同じプロパティを持っています。
混乱した

18
@MatthewRock-いいえ、あなたはリンクされたリストについて話しているのですが、質問や他の誰もが一般的なリストインターフェースについて話しているときです。
DavorŽdralo

5
自動プロパティは、C#の特徴的な機能(get; set;構文)です。また、リストクラスの命名。
jaypb

回答:


37

TL; DR:多くの場合、不適切なデザインにつながるため、列挙型のコレクションを使用することは悪い考えです。通常、列挙型のコレクションは、特定のロジックを持つ個別のシステムエンティティを呼び出します。

enumのいくつかのユースケースを区別する必要があります。このリストは私の頭の一番上にあるので、もっと多くのケースがあるかもしれません...

例はすべてC#で記述されています。選択する言語は同様の構成要素を持っているか、自分で実装できる可能性があります。

1.単一の値のみが有効です

この場合、値は排他的です。例えば

public enum WorkStates
{
    Init,
    Pending,
    Done
}

両方のあるいくつかの作業を持って無効であるPendingとしますDone。したがって、これらの値の1つのみが有効です。これは、enumの適切な使用例です。

2.値の組み合わせは有効です。
この場合はフラグとも呼ばれ、C#は[Flags]これらを操作する列挙属性を提供します。アイデアはのセットboolまたはbit、それぞれが1つの列挙メンバーに対応する sの。各メンバーには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
}

列挙型メンバーのコレクションを使用すると、各列挙型メンバーが設定または設定解除された1ビットのみを表すような場合に過剰になります。ほとんどの言語はこのような構造をサポートしていると思います。それ以外の場合は、作成できます(例: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)値のいくつかの組み合わせは有効であり、いくつかは無効です

これはよくあるシナリオです。多くの場合、2つの異なるものを1つの列挙型に入れています。例:

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

それは両方のために完全に有効ですが今VehicleBuilding持っているDoorsおよびWindowSをするために、それは非常に普通のではないBuildingsが持っているWheelのを。

この場合、#1または#2a)のいずれかのケースを達成するために、enumを複数の部分に分割し、オブジェクト階層を変更することをお勧めします。

設計上の考慮事項

どういうわけか、エンティティのタイプは、enumによって通常提供される情報に類似していると見なすことができるため、enumはOOの駆動要素ではない傾向があります。

たとえば、IceCream#2からサンプルを取り、IceCreamエンティティはフラグの代わりにScoopオブジェクトのコレクションを持ちます。

それほど純粋ではないアプローチはScoopFlavor財産を持つことです。純粋主義のアプローチはScoop、の抽象基底クラスになることですVanillaScoopChocolateScoop代わりに、...クラス。

要点は次のとおり
です。1.「何かのタイプ」であるすべてのものを列挙する必要はありません
はない2.いくつかの列挙型メンバーが有効なフラグではない場合、列挙型を複数の異なる列挙型に分割することを検討してください。

今、あなたの例のために(わずかに変更されました):

public enum Role
{
    User,
    Admin
}

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

この正確なケースは次のようにモデル化されるべきだと思います(注:実際には拡張可能ではありません!):

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

言い換えれば、それは暗黙的であり、であることUserUser、追加情報は彼がAdminです。

あなたが排他的ではありません複数の役割を持って取得する場合(例えばUserすることができAdminModeratorVIP、...同時に)、それはどちらかのフラグが列挙型を使用するには良い時間やabtract基底クラスまたはインタフェースになります。

クラスを使用して表現することRoleによりRole、特定のアクションを実行できるかどうかを決定する責任を持つことができる責任の分離が改善されます。

列挙型を使用すると、すべてのロールのロジックを1か所にまとめる必要があります。これはオブジェクト指向の目的を打ち負かし、あなたを命令に戻します。

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

ただし、列挙型を使用することは、許容可能なアプローチ(たとえば、パフォーマンス)である場合があります。ここでの決定は、モデル化されたシステムの要件に依存します。


3
[Flags]intがそのタイプの40億の値がすべて有効であることを意味する以上に、すべての組み合わせが有効であることを意味するとは思わない。それは単に、単一のフィールド内でそれら結合できることを意味します-組み合わせに対する制限は、より高いレベルのロジックに属します。
Random832

警告:を使用[Flags]する場合、値を2のべき乗に設定する必要があります。そうしないと、期待どおりに動作しません。
アルトゥーロトーレスサンチェス

@ Random832私はそれを言うつもりはなかったが、答えを編集した。今ではもっと明確になっていることを願っている。
ズデニェクジェリネク

1
@ArturoTorresSánchezご意見ありがとうございます。答えを修正し、それについての説明も追加しました。
ズデニェクジェリネク

素晴らしい説明!実際にはフラグはシステム要件に非常によく適合しますが、既存のサービス実装のためにHashSetを使用する方が簡単です。
デクシー

79

Setを使用しないのはなぜですか?リストを使用する場合:

  1. 同じ役割を2回追加するのは簡単です
  2. リストの単純な比較はここでは正しく機能しません。[ユーザー、管理者]は[管理者、ユーザー]と同じではありません
  3. 交差やマージなどの複雑な操作は実装が簡単ではありません

パフォーマンスが懸念される場合、たとえば、Javaでは、EnumSetブールの固定長配列として実装されます(i番目のブール要素は、このセットにEnum値が存在するかどうかの質問に答えます)。たとえば、EnumSet<Role>。また、を参照してくださいEnumMap。C#に似たものがあると思います。


35
実際、JavaではEnumSetビットフィールドとして実装されます。
-biziclop

4
HashSet<MyEnum>vsフラグの列挙に関連するSOの質問:stackoverflow.com/q/9077487/87698-Heinzi
1

3
.NETにはFlagsAttribute、ビットごとの演算子を使用して列挙型を連結できるようになっています。Javaに似た音EnumSetmsdn.microsoft.com/en-US/LIBRARY/system.flagsattribute編集:1つの答えを見下ろすべきでした!これの良い例があります。
ps2goat

4

それらを結合できるように列挙型を書きます。基数2の指数関数を使用すると、それらをすべて1つの列挙型に結合し、1つのプロパティを持ち、それらをチェックできます。あなたのenumはこれをlileする必要があります

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

3
1を使用しない理由は何ですか?
ロビーディー

1
@RobbieDee効果的に1、2、4、8などを使用することはできませんでした。そうです
レミ

5
さて、Javaを使用する場合EnumSetenumsにこれを実装するJavaが既にあります。すべてのブール演算がすでに抽象化されており、さらに使いやすくするためのいくつかのメソッドに加えて、小さなメモリと優れたパフォーマンスがあります。
fr13d

2
@ fr13d私はac#男です。質問にはJavaタグがないため、この回答はより一般的に適用されると思います
レミ

1
そう、@Rémi。C#は[flags]属性を提供します。@ZdeněkJelínekは彼の投稿でこの属性を調べています。
fr13d

4

ユーザーに列挙値のリストを使用することは良い習慣ですか?


短い答え:はい


より良い短い答え:はい、enumはドメイン内の何かを定義します。


設計時の答え:ドメイン自体の観点からドメインをモデル化するクラス、構造などを作成して使用します。


コーディング時間の答え:列挙型をコードスリングする方法は次のとおりです...


推測される質問

  • 文字列を使用する必要がありますか?

    • 答え:いいえ。
  • 他のクラスを使用する必要がありますか?

    • さらに、はい。代わりに、いいえ。
  • あるRoleで使用するのに十分な列挙型のリストはUser

    • 何も思いつきません。証拠にない他の設計詳細に大きく依存しています。

WTFボブ?

  • これは、プログラミング言語の技術に組み込まれた設計上の質問です。

    • アンは、enumモデル内の「役割」を定義するための良い方法です。だから、List役割のは良いことです。
  • enum 文字列よりもはるかに優れています。

    • Role ドメインに存在するすべてのロールの宣言です。
    • 文字列「Admin」は、文字"A" "d" "m" "i" "n"がその順に並んだ文字列です。ドメインに関する限り、それは何でもありません
  • Role何らかの生命の概念を提供するために設計する可能性のあるクラス(機能性)は、Role列挙型をうまく活用できます。

    • たとえば、あらゆる種類のロールのサブクラスではなく、Roleこのクラスインスタンスがどのような種類のロールであるかを伝えるプロパティを用意します。
    • User.Roles我々が必要としない、または、インスタンス化されたロールオブジェクトをしたくないときリストが合理的です。
    • そして、ロールをインスタンス化する必要がある場合、enumはそれをRoleFactoryクラスに伝えるための明確なタイプセーフな方法です。そして、ささいなことではありませんが、コードリーダーに。

3

これは私が見たものではありませんが、うまくいかない理由はありません。ただし、並べ替えられたリストを使用して、だまされないようにすることができます。

より一般的なアプローチは、システム、管理者、パワーユーザー、ユーザーなどの単一のユーザーレベルです。システムはすべてを実行でき、ほとんどのことを管理できます。

ロールの値を2のべき乗として設定する場合、ロールをintとして保存し、本当に単一のフィールドに保存したいが、それが誰の好みにも合わない場合は、ロールを導出できます。

だからあなたは持っているかもしれません:

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

したがって、ユーザーのロール値が6の場合、フロントオフィスとウェアハウスのロールがあります。


2

重複を防ぎ、より多くの比較メソッドがあるため、HashSetを使用します

それ以外の場合はEnumの適切な使用

メソッドBoolean IsInRole(Role R)を追加します

そして、私はセットをスキップします

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

1

単純化した例は問題ありませんが、通常はそれよりも複雑です。たとえば、データベースでユーザーとロールを永続化する場合、enumを使用するのではなく、データベーステーブルでロールを定義する必要があります。これにより、参照整合性が得られます。


0

システム(ソフトウェア、オペレーションシステム、組織など)が役割と権限を扱う場合、役割を追加して権限を管理すると便利な場合があります。
ソースコードを変更することでこれをアーカイブできる場合は、列挙型に固執しても大丈夫かもしれません。しかし、ある時点で、システムのユーザーはロールと許可を管理できるようにしたいと考えています。列挙型が使用できなくなります。
代わりに、構成可能なデータ構造が必要になります。すなわち、ロールに割り当てられた許可セットも保持するUserRoleクラス。
Userクラスには一連のロールがあります。ユーザーの許可を確認する必要がある場合、すべての役割の許可の和集合が作成され、問題の許可が含まれているかどうかが確認されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.