私は現在、ユーザーがいて、各ユーザーが1つまたは複数の役割を持つシステムで作業しています。ユーザーに列挙値のリストを使用することは良い習慣ですか?私はこれ以上何も考えられませんが、これは問題を感じません。
enum Role{
Admin = 1,
User = 2,
}
class User{
...
List<Role> Roles {get;set;}
}
TL; DR:enumのコレクションを使用することは、悪い設計につながることが多いため、通常はお勧めできません。 enumのコレクションは通常、特定のロジックを持つ個別のシステムエンティティを必要とします。
列挙型のいくつかの使用例を区別する必要があります。このリストは私の頭の上にあるだけなので、もっとケースがあるかもしれません...
例はすべてC#で記述されています。選択した言語にも同様の構成要素があるか、または自分で実装することもできます。
1。単一の値のみが有効です
この場合、値は排他的です。
public enum WorkStates
{
Init,
Pending,
Done
}
Pending
とDone
の両方の作業を行うことは無効です。したがって、これらの値の1つだけが有効です。これは列挙型の良いユースケースです。
2。値の組み合わせは有効です
このケースはフラグとも呼ばれ、C#は[Flags]
これらを使用するための列挙型属性。アイデアは、それぞれが1つの列挙型メンバーに対応するbool
sまたはbit
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; }
}
ここで、Vehicle
とBuilding
の両方がDoor
sとWindow
sを持つことは完全に有効ですが、Building
sがWheel
sを持つことはあまり一般的ではありません。
この場合、ケース#1または#2a)のいずれかを実現するために、列挙型を部分に分割するか、オブジェクト階層を変更するか、あるいはその両方を行うことをお勧めします。
設計上の考慮事項
エンティティのタイプは、通常、列挙型によって提供される情報に類似していると見なすことができるため、どういうわけか、列挙型はOOの駆動要素ではない傾向があります。
たとえば#2のIceCream
サンプルでは、IceCream
エンティティはフラグの代わりにScoop
オブジェクトのコレクションを持っています。
それほど純粋ではないアプローチは、Scoop
がFlavor
プロパティを持つことです。純粋主義的なアプローチは、Scoop
をVanillaScoop
、ChocolateScoop
、...クラスの抽象基本クラスにすることです。
一番下の行はそれです:
1。 「何かのタイプ」であるすべてが列挙型である必要はありません
2。一部のシナリオで一部の列挙型メンバーが有効なフラグではない場合は、列挙型を複数の異なる列挙型に分割することを検討してください。
今あなたの例のために(少し変更されました):
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
、...にすることができます)、フラグ列挙型または抽象基本クラスまたはインターフェイスを使用するのに適しています。
クラスを使用してRole
を表すと、Role
が特定のアクションを実行できるかどうかを決定する責任を負うことができるため、責任の分離が向上します。
列挙型では、すべてのロールのロジックを1か所に配置する必要があります。これはOOの目的を無効にし、あなたを命令に戻します。
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));
}
}
ただし、列挙型の使用は許容できるアプローチです(パフォーマンスなど)。ここでの決定は、モデル化されたシステムの要件によって異なります。
ユーザーに列挙値のリストを使用することは良い習慣ですか?
短い答え:はい
より良い答え:はい、列挙型はドメイン内で何かを定義します。
設計時の回答:ドメイン自体に関してドメインをモデル化するクラス、構造などを作成して使用します。
コーディング時間の回答:列挙型をコードスリングする方法は次のとおりです...
推定された質問:
文字列を使用する必要がありますか?
他のクラスを使用する必要がありますか?
Role
列挙リストはUser
で使用するのに十分ですか?
WTFボブ?
これは、プログラミング言語の技術に組み込まれた設計上の質問です。
enum
は、モデルで「役割」を定義するための良い方法です。したがって、List
の役割は良いことです。enum
は文字列よりもはるかに優れています。
Role
は、ドメインに存在するすべてのロールの宣言です"A"
"d"
"m"
"i"
"n"
という文字がこの順序で含まれている文字列です。ドメインに関する限り、nothingです。Role
の概念にいくつかの命を与えるために設計する可能性のあるクラス-機能性-は、Role
列挙型をうまく利用できます。
Role
プロパティを用意します。User.Roles
リストは、インスタンス化されたロールオブジェクトを必要としない場合、または必要としない場合に適しています。RoleFactory
クラスに伝達する明確でタイプセーフな方法です。そして、取るに足らないことではないが、コードリーダーにとっては。それらを組み合わせることができるように列挙型を記述します。基数2の指数関数を使用すると、それらをすべて1つの列挙型に結合し、1つのプロパティを持ち、それらをチェックできます。あなたの列挙型はこれをリリーにする必要があります
enum MyEnum
{
FIRST_CHOICE = 2,
SECOND_CHOICE = 4,
THIRD_CHOICE = 8
}
それは私が見たものではありませんが、それが機能しない理由はわかりません。ただし、並べ替えられたリストを使用して、だまされないようにすることができます。
より一般的なアプローチは、シングルユーザーレベルです。 system、admin、power user、userなど。システムはすべてを実行でき、ほとんどのことを管理できます。
ロールの値を2の累乗として設定する場合、ロールをintとして保存し、それらを本当に1つのフィールドに保存したいが、それがすべての人の好みではない場合は、ロールを派生させることができます。
だからあなたは持っているかもしれません:
Back office role 1
Front office role 2
Warehouse role 4
Systems role 8
したがって、ユーザーの役割の値が6の場合、フロントオフィスと倉庫の役割が割り当てられます。
HashSet を使用すると、重複を防ぎ、比較方法が増えます。
そうでなければEnumをうまく使う
メソッドBoolean IsInRole(Role R)を追加します
そして、私はセットをスキップします
List<Role> roles = new List<Role>();
Public List<Role> Roles { get {return roles;} }
あなたが与える単純化した例は結構ですが、通常それよりも複雑です。たとえば、ユーザーとロールをデータベースに永続化する場合は、列挙型を使用するのではなく、データベーステーブルにロールを定義する必要があります。これにより、参照整合性が提供されます。
システム(ソフトウェア、オペレーティングシステム、組織など)が役割と権限を扱う場合、役割を追加してそれらの権限を管理することが役立つ場合があります。
これをソースコードを変更してアーカイブできる場合は、列挙型を使用しても問題ありません。しかし、ある時点で、システムのユーザーは役割と権限を管理できるようになりたいと考えています。列挙型は使用できなくなります。
代わりに、構成可能なデータ構造が必要になります。つまり、役割に割り当てられた一連の権限も保持するUserRoleクラス。
Userクラスには、一連のロールがあります。ユーザーの権限を確認する必要がある場合は、すべてのロール権限の和集合が作成され、問題の権限が含まれているかどうかが確認されます。