현재 사용자가있는 시스템에서 작업하고 있으며 각 사용자에게는 하나 이상의 역할이 있습니다. User에서 Enum 값 목록을 사용하는 것이 좋습니다? 나는 더 나은 것을 생각할 수 없지만 이것은 기분이 좋지 않습니다.
enum Role{
Admin = 1,
User = 2,
}
class User{
...
List<Role> Roles {get;set;}
}
현재 사용자가있는 시스템에서 작업하고 있으며 각 사용자에게는 하나 이상의 역할이 있습니다. User에서 Enum 값 목록을 사용하는 것이 좋습니다? 나는 더 나은 것을 생각할 수 없지만 이것은 기분이 좋지 않습니다.
enum Role{
Admin = 1,
User = 2,
}
class User{
...
List<Role> Roles {get;set;}
}
답변:
TL; DR : 일반적으로 디자인이 잘못되기 때문에 열거 형 모음을 사용하는 것은 좋지 않습니다. 열거 형 모음은 일반적으로 특정 논리를 가진 고유 한 시스템 엔터티를 요구합니다.
열거 형의 몇 가지 사용 사례를 구별해야합니다. 이 목록은 내 머리 위에 만 있으므로 더 많은 사례가있을 수 있습니다 ...
예제는 모두 C #에 있으며, 선택한 언어가 비슷한 구문을 갖거나 직접 구현할 수 있습니다.
1. 단일 값만 유효합니다
이 경우 값은 배타적입니다. 예 :
public enum WorkStates
{
Init,
Pending,
Done
}
Pending
과 (과 ) 모두 같은 작업을하는 것은 유효하지 않습니다 Done
. 따라서이 값 중 하나만 유효합니다. 이것은 열거 형의 좋은 사용 사례입니다.
2. 값의 조합이 유효합니다.
이 경우는 플래그라고도하며 C #은 [Flags]
이러한 작업에 열거 형 속성을 제공합니다 . 아이디어는 각각 하나의 열거 형 멤버에 해당하는 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
}
열거 형 멤버 컬렉션을 사용하는 것은 각 열거 형 멤버가 설정되거나 설정되지 않은 하나의 비트 만 나타내는 경우에 무리가 있습니다. 대부분의 언어가 이와 같은 구문을 지원한다고 생각합니다. 그렇지 않으면 하나를 만들 수 있습니다 (예 :을 사용 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) 일부 값의 조합은 유효하고 일부는 유효하지 않습니다
이것은 빈번한 시나리오입니다. 하나의 열거 형에 두 가지 다른 것을 넣는 경우가 종종 있습니다. 예:
[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
의를 위해, 아주 보통이 아니다 Building
들 가지고 Wheel
들.
이 경우, 열거 # 1 또는 # 2a를 달성하기 위해 열거를 부분으로 나누거나 객체 계층을 수정하는 것이 좋습니다.
디자인 고려 사항
어쨌든 열거 형은 OO의 추진 요소가 아닌 경향이 있습니다. 엔티티의 유형은 일반적으로 열거 형이 제공하는 정보와 유사하게 간주 될 수 있기 때문입니다.
예를 들어 IceCream
# 2 의 샘플을 가져 오면 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
A는 User
, 추가 정보는 그가 여부입니다 Admin
.
당신이 배타적이지 여러 역할을 할 수있는 경우 (예를 들어이 User
될 수있다 Admin
, Moderator
, VIP
, ... 같은 시간에), 그 중 하나가 플래그 열거 형 사용하는 것이 시간 또는 abtract 기본 클래스 또는 인터페이스가 될 것입니다.
클래스를 사용하여 주어진 조치를 수행 할 수 있는지 여부를 결정할 책임이있는 책임을 Role
더 잘 분리 Role
합니다.
열거 형을 사용하면 모든 역할에 대해 한곳에 논리가 있어야합니다. 어느 것이 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));
}
}
열거 형을 사용하는 것이 허용 가능한 접근 방법 일 수 있습니다 (예 : 성능). 여기서 결정은 모델링 된 시스템의 요구 사항에 따라 다릅니다.
[Flags]
모든 조합이 유효 하다는 것을 의미 한다고 생각하지 않습니다 .int는 그 유형의 40 억 값이 모두 유효하다는 것을 의미합니다. 이는 단순히 단일 필드 내에서 결합 될 수 있음을 의미 합니다. 조합에 대한 제한은 상위 논리에 속합니다.
[Flags]
값을 2의 거듭 제곱으로 설정해야합니다. 그렇지 않으면 예상대로 작동하지 않습니다.
왜 Set을 사용하지 않습니까? 목록을 사용하는 경우 :
예를 들어 Java와 같은 성능에 관심이 있다면 EnumSet
고정 길이의 부울 배열로 구현됩니다 (i 번째 부울 요소는 i'th Enum 값 이이 세트에 있는지 여부에 대한 질문에 대답합니다). 예를 들면 다음과 같습니다 EnumSet<Role>
. 또한을 참조하십시오 EnumMap
. C #에 비슷한 것이 있다고 생각합니다.
EnumSet
에서는 비트 필드로 구현됩니다.
HashSet<MyEnum>
플래그 열거 형 에 대한 관련 SO 질문 : stackoverflow.com/q/9077487/87698
FlagsAttribute
을 사용하여 비트 연산자를 사용하여 열거 형을 연결할 수 있습니다. 자바와 비슷한 소리 EnumSet
. msdn.microsoft.com/en-US/LIBRARY/system.flagsattribute 편집 : 하나의 답변을 살펴보아야합니다! 이것의 좋은 예가 있습니다.
열거 형을 작성하여 결합 할 수 있습니다. 기수 2 지수를 사용하면 하나의 열거 형으로 모두 결합하고 1 개의 속성을 가지며 확인할 수 있습니다. 당신의 열거 형은 이것이어야합니다.
enum MyEnum
{
FIRST_CHOICE = 2,
SECOND_CHOICE = 4,
THIRD_CHOICE = 8
}
EnumSet
이것을 구현 한 것이있다 enum
. 모든 부울 산술이 이미 추상화되었으며 사용하기 쉽게 만드는 다른 방법과 작은 메모리 및 우수한 성능이 있습니다.
User에서 Enum 값 목록을 사용하는 것이 좋습니다?
짧은 답변 : 예
더 나은 짧은 대답 : 예, 열거 형은 도메인에서 무언가를 정의합니다.
디자인 타임 답변 : 도메인 자체를 기준으로 도메인을 모델링하는 클래스, 구조 등을 만들고 사용합니다.
코딩 시간 답변 : 다음은 열거 형을 코드 슬링하는 방법입니다 ...
유추 된 질문 :
문자열을 사용해야합니까?
다른 수업을 사용해야합니까?
는 IS Role
열거 목록에서 사용하기에 충분 User
?
WTF 밥?
이것은 프로그래밍 언어 기술에 짜여진 디자인 질문입니다.
enum
모델의 "역할"을 정의 할 수있는 좋은 방법입니다. 그렇다면 List
역할 중 하나는 좋은 것입니다.enum
현보다 훨씬 우수합니다.
Role
도메인에 존재하는 모든 역할의 선언입니다. "A"
"d"
"m"
"i"
"n"
은 순서대로 문자가 포함 된 문자열입니다 . 그것은 없다 아무것도 지금까지 도메인에 관한 한.Role
삶 의 일부 개념 ( 기능) 을 제공하도록 디자인 할 수있는 모든 클래스 는 Role
열거 형을 잘 활용할 수 있습니다 .
Role
가 아니라이 클래스 인스턴스가 어떤 역할을하는지 알려주 는 속성을 가지십시오.User.Roles
우리가 필요로하지 않는, 또는, 인스턴스화 된 역할 객체를하지 않으려는 경우 목록이 합리적이다.RoleFactory
. 그리고 코드 리더에게는 중요하지 않습니다.그것은 내가 본 것이 아니지만 그것이 작동하지 않는 이유를 알 수 없습니다. 그러나 정렬 된 목록을 사용하여 중복을 방지 할 수 있습니다.
보다 일반적인 접근 방식은 단일 사용자 수준입니다 (예 : 시스템, 관리자, 고급 사용자, 사용자 등). 시스템은 모든 것을 수행하고 대부분의 작업을 관리 할 수 있습니다.
역할의 값을 2의 거듭 제곱으로 설정하려면 역할을 int로 저장하고 실제로 단일 필드에 저장하고 싶지만 모든 사람의 취향에 맞지 않을 수있는 역할을 파생시킬 수 있습니다.
그래서 당신은 가질 수 있습니다 :
Back office role 1
Front office role 2
Warehouse role 4
Systems role 8
따라서 사용자의 역할 값이 6이면 프론트 오피스 및웨어 하우스 역할이 있습니다.
시스템 (소프트웨어, 운영 체제 또는 조직)이 역할 및 권한을 처리하는 경우 역할을 추가하고 해당 시스템의 권한을 관리하는 것이 유용한 시점이 있습니다.
소스 코드를 변경하여 아카이브 할 수 있다면 열거 형을 고수하는 것이 좋습니다. 그러나 어느 시점에서 시스템 사용자는 역할과 권한을 관리 할 수 있기를 원합니다. 열거 형을 사용할 수 없게됩니다.
대신 구성 가능한 데이터 구조를 원할 것입니다. 즉, 역할에 할당 된 일련의 권한을 보유한 UserRole 클래스.
User 클래스에는 일련의 역할이 있습니다. 사용자의 권한을 확인해야하는 경우 모든 역할 권한의 통합 집합이 만들어지고 해당 권한이 포함되어 있는지 확인합니다.