Atualmente, estou trabalhando em um sistema em que há usuários e cada usuário tem uma ou várias funções. É uma boa prática usar os valores da Lista de Enum no Usuário? Não consigo pensar em nada melhor, mas isso não parece certo.
enum Role{
Admin = 1,
User = 2,
}
class User{
...
List<Role> Roles {get;set;}
}
Respostas:
TL; DR: Geralmente, é uma má idéia usar uma coleção de enumerações, pois geralmente leva a um design inadequado. Uma coleção de enums geralmente requer entidades distintas do sistema com lógica específica.
É necessário distinguir entre alguns casos de uso de enum. Esta lista é apenas do topo da minha cabeça, então pode haver mais casos ...
Os exemplos estão todos em C #, acho que sua linguagem de escolha terá construções semelhantes ou seria possível implementá-las você mesmo.
1. Apenas um valor único é válido
Nesse caso, os valores são exclusivos, por exemplo
É inválido ter algum trabalho que seja ambos
Pending
eDone
. Portanto, apenas um desses valores é válido. Este é um bom caso de uso de enum.2. Uma combinação de valores é válida.
Este caso também é chamado de sinalizadores, o C # fornece o
[Flags]
atributo enum para trabalhar com eles. A ideia pode ser modelada como um conjunto debool
s oubit
s, cada um correspondendo a um membro da enumeração. Cada membro deve ter um valor de poder de dois. As combinações podem ser criadas usando operadores bit a bit:O uso de uma coleção de membros da enumeração é um exagero, pois cada membro da enumeração representa apenas um bit que está definido ou não. Eu acho que a maioria dos idiomas suporta construções como esta. Caso contrário, você pode criar um (por exemplo, use
bool[]
e resolva-o com(1 << (int)YourEnum.SomeMember) - 1
).a) Todas as combinações são válidas
Embora isso esteja correto em alguns casos simples, uma coleção de objetos pode ser mais apropriada, pois muitas vezes você precisa de informações ou comportamentos adicionais com base no tipo.
(nota: isso pressupõe que você realmente se preocupa apenas com os sabores do sorvete - que não precisa modelar o sorvete como uma coleção de bolas e um cone)
b) Algumas combinações de valores são válidas e outras não
Este é um cenário frequente. O caso pode ser que você esteja colocando duas coisas diferentes em uma enumeração. Exemplo:
Agora, embora seja completamente válido para ambos
Vehicle
eBuilding
tenhaDoor
s eWindow
s, não é muito comum queBuilding
s tenhaWheel
s.Nesse caso, seria melhor dividir o enum em partes e / ou modificar a hierarquia de objetos para alcançar o caso nº 1 ou nº 2a).
Considerações de design
De alguma forma, as enumerações tendem a não ser os elementos determinantes no OO, pois o tipo de uma entidade pode ser considerado semelhante às informações que geralmente são fornecidas por uma enumeração.
Pegue, por exemplo, a
IceCream
amostra do item 2, aIceCream
entidade teria, em vez de sinalizadores, uma coleção deScoop
objetos.A abordagem menos purista seria a
Scoop
de ter umaFlavor
propriedade. A abordagem purista seria oScoop
de ser uma classe base abstrata paraVanillaScoop
,ChocolateScoop
, ... aulas em seu lugar.A linha inferior é que:
1. Nem tudo que é um "tipo de algo" precisa ser enum
2. Quando algum membro da enum não é um sinalizador válido em algum cenário, considere dividir a enum em várias enumerações distintas.
Agora, para o seu exemplo (ligeiramente alterado):
Eu acho que esse caso exato deve ser modelado como (nota: não é realmente extensível!):
Em outras palavras - está implícito que
User
é umUser
, a informação adicional é se ele é umAdmin
.Se você começa a ter múltiplos papéis que não são exclusivos (por exemplo,
User
pode serAdmin
,Moderator
,VIP
, ... ao mesmo tempo), isso seria um bom momento para usar tanto bandeiras enum ou uma classe base abtract ou interface.O uso de uma classe para representar uma
Role
leva a uma melhor separação de responsabilidades, onde a pessoaRole
pode ter a responsabilidade de decidir se pode executar uma determinada ação.Com uma enumeração, você precisa ter a lógica em um só lugar para todas as funções. O que derrota o objetivo do OO e o leva de volta ao imperativo.
Imagine que a
Moderator
possui direitos de edição eAdmin
possui direitos de edição e exclusão.Abordagem enum (chamada
Permissions
para não misturar funções e permissões):Abordagem de classe (isso está longe de ser perfeito, idealmente, você desejaria o
IAction
padrão de visitante, mas este post já é enorme ...):Usar um enum pode ser uma abordagem aceitável (por exemplo, desempenho). A decisão aqui depende dos requisitos do sistema modelado.
fonte
[Flags]
implique que todas as combinações sejam válidas, assim como um int implica que todos os quatro bilhões de valores desse tipo sejam válidos. Significa simplesmente que eles podem ser combinados em um único campo - quaisquer restrições às combinações pertencem à lógica de nível superior.[Flags]
, você deve definir os valores para potências de dois, caso contrário, não funcionará conforme o esperado.Por que não usar Set? Se estiver usando a Lista:
Se você está preocupado com desempenho, por exemplo, em Java, existe um
EnumSet
que é implementado como uma matriz de comprimento fixo de booleanos (o elemento booleano responde à pergunta se o valor Enum está presente neste conjunto ou não). Por exemploEnumSet<Role>
,. Veja tambémEnumMap
. Eu suspeito que c # tem algo semelhante.fonte
EnumSet
s são implementados como campos de bits.HashSet<MyEnum>
bandeiras vs. enums: stackoverflow.com/q/9077487/87698FlagsAttribute
, que permite usar operadores bit a bit para concatenar enumerações. Soa semelhante ao JavaEnumSet
. msdn.microsoft.com/en-US/LIBRARY/system.flagsattribute EDIT: Eu deveria ter procurado uma resposta! tem um bom exemplo disso.Escreva seu enum para poder combiná-los. Usando o exponencial base 2, você pode combiná-los todos em uma enumeração, ter 1 propriedade e verificar por eles. Sua enumeração deve demorar um pouco
fonte
EnumSet
que implementa isso paraenum
s. Você tem toda a aritmética booleana já abstraída, além de outros métodos para facilitar o uso, além de pouca memória e bom desempenho.[flags]
atributo que @ Zdeněk Jelínek explora em seu post .Resposta curta : Sim
Melhor resposta curta : Sim, o enum define algo no domínio.
Resposta em tempo de design : crie e use classes, estruturas etc. que modelam o domínio em termos do próprio domínio.
Resposta em tempo de codificação : veja como codificar enums de sling ...
As perguntas inferidas :
Devo usar strings?
Devo usar alguma outra classe?
A
Role
lista de enumerações é suficiente para usoUser
?WTF Bob?
Essa é uma questão de design que está enquadrada nos aspectos técnicos da linguagem de programação.
enum
é uma boa maneira de definir "Funções" no seu modelo. Então, umList
dos papéis é uma coisa boa.enum
é muito superior às cordas.Role
é uma declaração de TODAS as funções existentes no domínio."A"
"d"
"m"
"i"
"n"
, nessa ordem. Não é nada no que diz respeito ao domínio.Quaisquer classes que você possa criar para fornecer o conceito de
Role
vida - funcionalidade - podem fazer bom uso daRole
enumeração.Role
propriedade que indique que tipo de função é essa instância de classe.User.Roles
lista é razoável quando não precisamos ou não queremos objetos de função instanciados.RoleFactory
classe; e, de maneira não insignificante, para o leitor de código.fonte
Isso não é algo que eu já vi, mas não vejo razão para que não funcione. Você pode querer usar a lista classificada para que ela se defenda contra os enganadores.
Uma abordagem mais comum é um nível de usuário único, por exemplo, sistema, administrador, usuário avançado, usuário etc. O sistema pode fazer tudo, administrar a maioria das coisas e assim por diante.
Se você definir os valores das funções como potências de dois, poderá armazená-la como int e derivá-las se realmente quiser armazená-las em um único campo, mas isso pode não ser do gosto de todos.
Então você pode ter:
Portanto, se um usuário tivesse um valor de função 6, ele teria as funções Front office e Warehouse.
fonte
Use o HashSet , pois evita duplicatas e possui mais métodos de comparação
Caso contrário, o bom uso do Enum
Adicionar um método Boolean IsInRole (Role R)
e eu pularia o set
fonte
O exemplo simplista que você dá é bom, mas geralmente é mais complicado que isso. Por exemplo, se você deseja persistir os usuários e funções em um banco de dados, defina as funções em uma tabela de banco de dados em vez de usar enumerações. Isso lhe dá integridade referencial.
fonte
Se um sistema - seja um software, um sistema operacional ou uma organização - lida com funções e permissões, chega um momento em que é útil adicionar funções e gerenciar permissões para elas.
Se isso puder ser arquivado alterando o código-fonte, pode ser bom ficar com enumerações. Mas, em algum momento, o usuário do sistema deseja gerenciar funções e permissões. Do que enums tornam-se inutilizáveis.
Em vez disso, você desejará ter uma estrutura de dados configurável. ou seja, uma classe UserRole que também possui um conjunto de permissões atribuídas à função.
Uma classe de usuário teria um conjunto de funções. Se houver a necessidade de verificar a permissão do usuário, um conjunto de união de todas as permissões de funções é criado e verificado se contém a permissão em questão.
fonte