Actualmente estoy trabajando en un sistema donde hay usuarios, y cada usuario tiene uno o varios roles. ¿Es una buena práctica usar la Lista de valores de Enum en el Usuario? No puedo pensar en nada mejor, pero esto no se siente bien.
enum Role{
Admin = 1,
User = 2,
}
class User{
...
List<Role> Roles {get;set;}
}
Respuestas:
TL; DR: Por lo general, es una mala idea usar una colección de enumeraciones, ya que a menudo conduce a un mal diseño. Una colección de enumeraciones generalmente requiere entidades del sistema distintas con lógica específica.
Es necesario distinguir entre algunos casos de uso de enum. Esta lista es solo la parte superior de mi cabeza, por lo que podría haber más casos ...
Todos los ejemplos están en C #, supongo que su lenguaje de elección tendrá construcciones similares o podría implementarlas usted mismo.
1. Solo un valor único es válido
En este caso, los valores son exclusivos, p. Ej.
No es válido tener un trabajo que sea a la vez
Pending
yDone
. Por lo tanto, solo uno de estos valores es válido. Este es un buen caso de uso de enum.2. Una combinación de valores es válida
Este caso también se llama banderas, C # proporciona un
[Flags]
atributo de enumeración para trabajar con estos. La idea se puede modelar como un conjunto debool
sobit
s con cada uno correspondiente a un miembro enum. Cada miembro debe tener un valor de poder de dos. Se pueden crear combinaciones usando operadores bit a bit:El uso de una colección de miembros de enumeración es una exageración en tal caso, ya que cada miembro de enumeración solo representa un bit que está activado o desactivado. Supongo que la mayoría de los idiomas admiten construcciones como esta. De lo contrario, puede crear uno (por ejemplo, usarlo
bool[]
y abordarlo(1 << (int)YourEnum.SomeMember) - 1
).a) Todas las combinaciones son válidas.
Si bien estos están bien en algunos casos simples, una colección de objetos puede ser más apropiada ya que a menudo necesita información adicional o comportamiento basado en el tipo.
(nota: esto supone que solo te importan realmente los sabores del helado, que no necesitas modelar el helado como una colección de bolas y un cono)
b) Algunas combinaciones de valores son válidas y otras no
Este es un escenario frecuente. El caso a menudo podría ser que estás poniendo dos cosas diferentes en una enumeración. Ejemplo:
Ahora, si bien es completamente válido para ambos
Vehicle
yBuilding
para tenerDoor
s yWindow
s, no es muy común queBuilding
s tengaWheel
s.En este caso, sería mejor dividir la enumeración en partes y / o modificar la jerarquía de objetos para lograr el caso # 1 o # 2a).
Consideraciones de diseño
De alguna manera, las enumeraciones tienden a no ser los elementos impulsores en OO ya que el tipo de entidad puede considerarse similar a la información que generalmente proporciona una enumeración.
Tome, por ejemplo, la
IceCream
muestra del n. ° 2, laIceCream
entidad en lugar de banderas tendría una colecciónScoop
objetos.El enfoque menos purista sería
Scoop
tener unaFlavor
propiedad. El enfoque purista sería que elScoop
ser una clase base abstracta paraVanillaScoop
,ChocolateScoop
, ... clases en su lugar.La conclusión es que:
1. No todo lo que es un "tipo de algo" debe ser enumeración
2. Cuando algún miembro de enumeración no es un indicador válido en algún escenario, considere dividir la enumeración en varias enumeraciones distintas.
Ahora para su ejemplo (ligeramente cambiado):
Creo que este caso exacto debería modelarse como (nota: ¡no es realmente extensible!):
En otras palabras, está implícito que el
User
es unUser
, la información adicional es si él es unAdmin
.Si se llega a tener múltiples roles que no son exclusivos (por ejemplo,
User
puede haberAdmin
,Moderator
,VIP
, ... al mismo tiempo), que sería un tiempo bueno para usar cualquiera de las banderas de enumeración o una clase base abtract o interfaz.El uso de una clase para representar a
Role
conduce a una mejor separación de responsabilidades donde unRole
puede tener la responsabilidad de decidir si puede realizar una acción determinada.Con una enumeración, necesitaría tener la lógica en un solo lugar para todos los roles. Lo cual derrota el propósito de OO y lo lleva de vuelta al imperativo.
Imagine que a
Moderator
tiene derechos de edición yAdmin
tiene derechos de edición y eliminación.Enum enfoque (llamado
Permissions
para no mezclar roles y permisos):Enfoque de clase (esto está lejos de ser perfecto, idealmente, desearía
IAction
un patrón de visitante pero esta publicación ya es enorme ...):Sin embargo, el uso de una enumeración puede ser un enfoque aceptable (por ejemplo, rendimiento). La decisión aquí depende de los requisitos del sistema modelado.
fuente
[Flags]
implique que todas las combinaciones sean válidas más de lo que un int implica que los cuatro mil millones de valores de ese tipo son válidos. Simplemente significa que se pueden combinar dentro de un solo campo: cualquier restricción en las combinaciones pertenece a la lógica de nivel superior.[Flags]
, debe establecer los valores en potencias de dos, de lo contrario no funcionará como se esperaba.¿Por qué no usar Set? Si usa List:
Si le preocupa el rendimiento, entonces, por ejemplo, en Java, existe el
EnumSet
que se implementa como una matriz de longitud fija de booleanos (el elemento booleano responde a la pregunta de si el valor Enum está presente en este conjunto o no). Por ejemplo,EnumSet<Role>
. Además, verEnumMap
. Sospecho que C # tiene algo similar.fuente
EnumSet
s se implementan como campos de bits.HashSet<MyEnum>
enumeraciones vs. banderas: stackoverflow.com/q/9077487/87698FlagsAttribute
, que le permite usar operadores bit a bit para concat enumeraciones. Suena similar al JavaEnumSet
. msdn.microsoft.com/en-US/LIBRARY/system.flagsattribute EDIT: ¡Debería haber buscado una respuesta! Tiene un buen ejemplo de esto.Escriba su enumeración para que pueda combinarlos. Al usar la base 2 exponencial, puede combinarlos todos en una enumeración, tener 1 propiedad y puede verificarlos. Tu enumeración debería ser lile esto
fuente
EnumSet
que implementa esto paraenum
s. Tiene toda la aritmética booleana ya abstraída, además de algunos otros métodos para que sea más fácil de usar, además de memoria pequeña y buen rendimiento.[flags]
atributo, que @ Zdeněk Jelínek explora en su publicación .Respuesta corta : sí
Mejor respuesta corta : Sí, la enumeración define algo en el dominio.
Respuesta en tiempo de diseño : crea y usa clases, estructuras, etc. que modelen el dominio en términos del dominio mismo.
Respuesta de tiempo de codificación : aquí se explica cómo codificar las enumeraciones ...
Las preguntas inferidas :
¿Debo usar cadenas?
¿Debo usar alguna otra clase?
¿La
Role
lista enum es suficiente para usarUser
?WTF Bob?
Esta es una pregunta de diseño que se enmarca en los tecnicismos del lenguaje de programación.
enum
es una buena manera de definir "Roles" en su modelo. Entonces, unaList
de las funciones es algo bueno.enum
Es muy superior a las cuerdas.Role
es una declaración de TODOS los roles que existen en el dominio."A"
"d"
"m"
"i"
"n"
, en ese orden. No es nada en lo que respecta al dominio.Cualquier clase que pueda diseñar para dar el concepto de
Role
vida (funcionalidad) puede hacer un buen uso de laRole
enumeración.Role
propiedad que indique qué tipo de rol es esta instancia de clase.User.Roles
lista es razonable cuando no necesitamos, o no queremos, objetos de roles instanciados.RoleFactory
clase; y, no insignificantemente, para el lector de código.fuente
Eso no es algo que haya visto, pero no veo ninguna razón por la que no funcionaría. Sin embargo, es posible que desee usar una lista ordenada para que se defienda contra los engañados.
Un enfoque más común es un nivel de usuario único, por ejemplo, sistema, administrador, usuario avanzado, usuario, etc. El sistema puede hacer todo, administrar la mayoría de las cosas, etc.
Si estableciera los valores de los roles como poderes de dos, podría almacenar el rol como int y derivar los roles si realmente quisiera almacenarlos en un solo campo, pero eso podría no ser del gusto de todos.
Entonces podrías tener:
Entonces, si un usuario tiene un valor de rol de 6, entonces tendría los roles de Front office y Warehouse.
fuente
Use HashSet ya que evita duplicados y tiene más métodos de comparación
De lo contrario, buen uso de Enum
Agregar un método Boolean IsInRole (Rol R)
y me saltaría el set
fuente
El ejemplo simplista que das está bien, pero generalmente es más complicado que eso. Por ejemplo, si va a persistir los usuarios y roles en una base de datos, debe definir los roles en una tabla de base de datos en lugar de usar enumeraciones. Eso te da integridad referencial.
fuente
Si un sistema, podría ser un software, un sistema operativo o una organización, trata con roles y permisos, llega un momento en que es útil agregar roles y administrar permisos para ellos.
Si esto puede archivarse alterando el código fuente, podría estar bien, seguir con las enumeraciones. Pero en algún momento el usuario del sistema quiere poder administrar roles y permisos. Que las enumeraciones se vuelven inutilizables.
En su lugar, querrá tener una estructura de datos configurable. es decir, una clase UserRole que también posee un conjunto de permisos asignados al rol.
Una clase de usuario tendría un conjunto de roles. Si es necesario verificar el permiso del usuario, se crea un conjunto de unión de todos los permisos de roles y se verifica si contiene el permiso en cuestión.
fuente