Obecnie pracuję w systemie, w którym są Użytkownicy, a każdy użytkownik ma jedną lub wiele ról. Czy dobrą praktyką jest używanie wartości Lista Enum na Użytkowniku? Nie mogę wymyślić nic lepszego, ale to nie jest w porządku.
enum Role{
Admin = 1,
User = 2,
}
class User{
...
List<Role> Roles {get;set;}
}
Odpowiedzi:
TL; DR: Zwykle złym pomysłem jest stosowanie zbioru wyliczeń, ponieważ często prowadzi to do złego projektu. Zbiór wyliczeń zwykle wymaga odrębnych elementów systemowych o określonej logice.
Konieczne jest rozróżnienie kilku przypadków użycia wyliczenia. Ta lista jest tylko z góry mojej głowy, więc może być więcej przypadków ...
Wszystkie przykłady są w języku C #, domyślam się, że twój wybrany język będzie miał podobne konstrukcje lub byłoby możliwe, abyś sam je zaimplementował.
1. Poprawna jest tylko jedna wartość
W takim przypadku wartości są wyłączne, np
Nieprawidłowe jest mieć pracę, która jest jednocześnie
Pending
iDone
. Dlatego tylko jedna z tych wartości jest poprawna. Jest to dobry przypadek użycia wyliczenia.2. Poprawna jest kombinacja wartości.
Przypadek ten nazywa się także flagami, C # zapewnia
[Flags]
atrybut enum do pracy z nimi. Pomysł można modelować jako zbiórbool
s lubbit
s, z których każdy odpowiada jednemu członowi wyliczającemu. Każdy członek powinien mieć wartość potęgi dwóch. Kombinacje można tworzyć za pomocą operatorów bitowych:Korzystanie ze zbioru elementów wyliczających jest w takim przypadku przesadą, ponieważ każdy element wyliczający reprezentuje tylko jeden bit, który jest albo ustawiony, albo nieuzbrojony. Chyba większość języków obsługuje takie konstrukcje. W przeciwnym razie możesz je utworzyć (np. Użyć
bool[]
i rozwiązać przez(1 << (int)YourEnum.SomeMember) - 1
).a) Wszystkie kombinacje są ważne
Chociaż są one w porządku w niektórych prostych przypadkach, kolekcja obiektów może być bardziej odpowiednia, ponieważ często potrzebujesz dodatkowych informacji lub zachowania w zależności od typu.
(uwaga: zakłada się, że naprawdę zależy ci tylko na smakach lodów - że nie musisz modelować lodów jako kolekcji gałek i stożka)
b) Niektóre kombinacje wartości są prawidłowe, a niektóre nie
To częsty scenariusz. Często może się zdarzyć, że umieścisz dwie różne rzeczy w jednym wyliczeniu. Przykład:
Teraz, mimo że jest w pełni poprawny zarówno dla, jak
Vehicle
iBuilding
dlaDoor
si iWindow
s, nie jest to zbyt często dlaBuilding
s i dla nichWheel
.W takim przypadku lepiej byłoby rozbić wyliczenie na części i / lub zmodyfikować hierarchię obiektów, aby osiągnąć przypadek nr 1 lub nr 2a.
Uwagi dotyczące projektu
W jakiś sposób wyliczenia zwykle nie są elementami napędzającymi w OO, ponieważ typ bytu można uznać za podobny do informacji, które zwykle dostarcza wyliczenie.
Weźmy np.
IceCream
Próbkę z nr 2,IceCream
jednostka zamiast flag miałaby kolekcjęScoop
obiektów.Mniej purystycznym podejściem byłoby
Scoop
posiadanieFlavor
własności. Podejście purist byłoby dlaScoop
być abstrakcyjną klasą bazową dlaVanillaScoop
,ChocolateScoop
... zamiast klas.Najważniejsze jest to, że:
1. Nie wszystko, co jest „rodzajem czegoś”, musi być wyliczone.
2. Jeśli jakiś element wyliczeniowy nie jest prawidłową flagą w niektórych scenariuszach, rozważ podzielenie wyliczenia na wiele różnych wyliczeń.
Teraz twój przykład (nieco zmieniony):
Myślę, że ten dokładny przypadek powinien zostać zamodelowany jako (uwaga: nie do rozszerzenia!):
Innymi słowy - domyślnie jest, że
User
jest toUser
, dodatkowe informacje to, czy on jestAdmin
.Jeśli masz mieć wiele ról, które nie wykluczają (np
User
może byćAdmin
,Moderator
,VIP
, ... w tym samym czasie), to byłby dobry czas, aby użyć jednej flagi ENUM lub abtract klasa bazowa lub interfejs.Wykorzystanie klasy do przedstawienia
Role
prowadzi do lepszego rozdzielenia obowiązków, w których osobaRole
może być odpowiedzialna za podjęcie decyzji, czy może wykonać dane działanie.Dzięki wyliczeniu musisz mieć logikę w jednym miejscu dla wszystkich ról. Który pokonuje cel OO i przywraca cię do imperatywu.
Wyobraź sobie, że a
Moderator
ma prawa do edycji orazAdmin
ma zarówno prawo do edycji , jak i do usuwania.Podejście Enum (wywoływane,
Permissions
aby nie mieszać ról i uprawnień):Podejście klasowe (jest to dalekie od ideału, idealnie, chciałbyś mieć
IAction
wzór dla odwiedzających, ale ten post jest już ogromny ...):Korzystanie z wyliczenia może być jednak akceptowalnym podejściem (np. Wydajność). Decyzja zależy od wymagań modelowanego systemu.
źródło
[Flags]
implikuje to, że wszystkie kombinacje są ważne tak samo jak int oznacza, że wszystkie cztery miliardy wartości tego typu są ważne. Oznacza to po prostu, że można je łączyć w jednym polu - wszelkie ograniczenia kombinacji należą do logiki wyższego poziomu.[Flags]
należy ustawić wartości na potęgę dwóch, w przeciwnym razie nie będzie działać zgodnie z oczekiwaniami.Dlaczego nie użyć zestawu? Jeśli używasz listy:
Jeśli
EnumSet
obawiasz się o wydajność, to na przykład w Javie jest taka, która jest implementowana jako tablica boolean o stałej długości (i-ty element boolowski odpowiada na pytanie, czy i-ta wartość Enum jest obecna w tym zestawie, czy nie). Na przykładEnumSet<Role>
. Zobacz takżeEnumMap
. Podejrzewam, że C # ma coś podobnego.źródło
EnumSet
są implementowane jako pola bitowe.HashSet<MyEnum>
enums vs. flags: stackoverflow.com/q/9077487/87698FlagsAttribute
, co pozwala używać operatorów bitowych do konkatowania wyliczeń. Brzmi podobnie do JavaEnumSet
. msdn.microsoft.com/en-US/LIBRARY/system.flagsattribute EDYCJA: Powinienem był spojrzeć w dół na jedną odpowiedź! ma tego dobry przykład.Napisz swoje wyliczenie, aby je połączyć. Używając wykładniczej podstawy 2, możesz połączyć je wszystkie w jednym wyliczeniu, mieć 1 właściwość i możesz je sprawdzić. Wasze wyliczenie powinno to znieść
źródło
EnumSet
implementującą to dlaenum
s. Wyodrębniono już całą arytmetykę logiczną, a także kilka innych metod ułatwiających jej użycie, a także niewielką pamięć i dobrą wydajność.[flags]
atrybut, który @ Zdeněk Jelínek bada w swoim poście .Krótka odpowiedź : tak
Lepsza krótka odpowiedź : tak, wyliczenie definiuje coś w dziedzinie.
Odpowiedź w czasie projektowania : Twórz i używaj klas, struktur itp., Które modelują domenę pod względem samej domeny.
Odpowiedź czasu kodowania : oto jak wyliczać procy ...
Wnioskowane pytania :
Czy powinienem używać ciągów znaków?
Czy powinienem użyć innej klasy?
Czy
Role
lista wyliczeń jest wystarczająca do użycia wUser
?WTF Bob?
To pytanie projektowe jest ujęte w szczegóły techniczne języka programowania.
enum
to dobry sposób na zdefiniowanie „Roli” w twoim modelu. Tak więc jednaList
z ról jest dobrą rzeczą.enum
jest znacznie lepszy od łańcuchów.Role
jest deklaracją WSZYSTKICH ról, które istnieją w domenie."A"
"d"
"m"
"i"
"n"
w tej kolejności. Jest to nic, jeśli chodzi o domenę.Wszelkie klasy, które zaprojektujesz, aby nadać koncepcji
Role
pewnej funkcji życiowej - mogą dobrze wykorzystaćRole
wyliczenie.Role
właściwość, która mówi, jaką rolę pełni ta instancja klasy.User.Roles
Lista jest uzasadnione, gdy nie trzeba, albo nie chcą, instancja obiektów ról.RoleFactory
klasie; i nie bez znaczenia dla czytnika kodów.źródło
Nie widziałem tego, ale nie widzę powodu, dla którego to by nie działało. Możesz jednak użyć posortowanej listy, która chroni ją jednak przed duplikatami.
Bardziej powszechnym podejściem jest poziom pojedynczego użytkownika, np. System, administrator, użytkownik zaawansowany, użytkownik itp. System może zrobić wszystko, administrować większością rzeczy i tak dalej.
Jeśli miałbyś ustawić wartości ról jako potęgi dwóch, możesz zapisać rolę jako int i wyprowadzić role, jeśli naprawdę chciałbyś przechowywać je w jednym polu, ale może to nie odpowiadać każdemu.
Więc możesz mieć:
Więc jeśli użytkownik miałby rolę o wartości 6, wówczas miałby role Front office i Warehouse.
źródło
Użyj HashSet, ponieważ zapobiega duplikatom i ma więcej metod porównywania
W przeciwnym razie dobre użycie Enum
Dodaj metodę Boolean IsInRole (Role R)
i pomijałem zestaw
źródło
Podany uproszczony przykład jest w porządku, ale zazwyczaj jest bardziej skomplikowany. Na przykład, jeśli zamierzasz utrwalić użytkowników i role w bazie danych, powinieneś zdefiniować role w tabeli bazy danych zamiast używać wyliczeń. To zapewnia integralność referencyjną.
źródło
Jeśli system - może to być oprogramowanie, system operacyjny lub organizacja - zajmuje się rolami i uprawnieniami, przychodzi czas, kiedy przydatne jest dodawanie ról i zarządzanie dla nich uprawnieniami.
Jeśli można to zarchiwizować poprzez zmianę kodu źródłowego, może być dobrze trzymać się wyliczeń. Ale w pewnym momencie użytkownik systemu chce mieć możliwość zarządzania rolami i uprawnieniami. Niż enum stają się bezużyteczne.
Zamiast tego będziesz chciał mieć konfigurowalną strukturę danych. tj. klasa UserRole, która również posiada zestaw uprawnień przypisanych do roli.
Klasa użytkownika miałaby zestaw ról. Jeśli zachodzi potrzeba sprawdzenia uprawnień użytkownika, tworzony jest łączny zestaw wszystkich uprawnień do uprawnień i sprawdzany, czy zawiera dane uprawnienie.
źródło