TL; DR: Genellikle kötü bir tasarıma yol açtığından, bir toplama koleksiyonu kullanmak genellikle kötü bir fikirdir. Enums koleksiyonu genellikle belirli mantıklara sahip farklı sistem varlıklarını gerektirir.
Birkaç kullanım enum durumu arasında ayrım yapmak gerekir. Bu liste sadece kafamın en üstünde, bu yüzden daha fazla dava olabilir ...
Örneklerin hepsi C # 'da, sanırım tercih ettiğiniz dil benzer yapılara sahip olacak veya bunları kendiniz uygulamanız mümkün olacak.
1. Sadece tek değer geçerlidir
Bu durumda, değerler münhasırdır;
public enum WorkStates
{
Init,
Pending,
Done
}
Hem bazı iş var geçersiz Pending
ve Done
. Bu nedenle bu değerlerden sadece biri geçerlidir. Bu iyi bir enum örneğidir.
2. Değerlerin bir kombinasyonu geçerlidir.
Bu duruma bayraklar da denir, C # [Flags]
bunlarla çalışmak için enum niteliği sağlar . Fikir, her biri bir enum üyesine karşılık gelen bir bool
s veya bit
s olarak modellenebilir . Her üyenin iki gücüne sahip olması gerekir. Bitsel operatörler kullanılarak kombinasyonlar oluşturulabilir:
[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
}
Enum üyeleri koleksiyonunun kullanılması, her enum üyesi yalnızca ayarlanmış veya ayarlanmamış bir biti temsil ettiği için böyle bir durumda geçersizdir. Sanırım çoğu dil bunun gibi yapıları destekliyor. Aksi takdirde bir tane oluşturabilirsiniz (örneğin, kullanıp bool[]
adresleyin (1 << (int)YourEnum.SomeMember) - 1
).
a) Tüm kombinasyonlar geçerlidir
Bazı basit durumlarda bunlar uygun olsa da, genellikle türüne bağlı olarak ek bilgi veya davranışa ihtiyaç duyduğunuz için bir nesne koleksiyonu daha uygun olabilir.
[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);
}
}
(not: bu, yalnızca dondurmanın lezzetlerini gerçekten umursadığınızı varsayar; dondurmayı kepçe ve bir külah koleksiyonu olarak modellemenize gerek yoktur)
b) Bazı değer kombinasyonları geçerlidir, bazıları değil
Bu sık sık bir senaryodur. Durum genellikle, iki farklı şeyi bir numaraya koymanız olabilir. Örnek:
[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; }
}
O ikisi için tamamen geçerli iken Şimdi Vehicle
ve Building
sahip olmak Door
s ve Window
s için, çok olağan değil Building
ler var Wheel
s.
Bu durumda, enumumu parçalara ayırmak ve / veya her iki durumda da # 1 veya # 2a) elde etmek için nesne hiyerarşisini değiştirmek daha iyi olacaktır.
Tasarım konuları
Her nasılsa, uçlar OO'daki itici elemanlar olma eğilimindedir, çünkü varlık tipi genellikle enum tarafından sağlanan bilgilere benzer olarak değerlendirilebilir.
IceCream
Örnek 2 # 'den bir örnek alın IceCream
, bayraklar yerine varlıkların bir Scoop
nesne koleksiyonu olur .
İçin daha az pürist yaklaşım olur Scoop
bir olması Flavor
özelliğini. İçin pürist yaklaşım olacağını Scoop
için soyut bir taban sınıfı olmak VanillaScoop
, ChocolateScoop
bunun yerine, ... sınıflar.
Alt satırda:
1. "Bir türden bir şey" olan her şeyin
enum olması gerekmiyor. 2. Bazı enum üyeleri bazı senaryolarda geçerli bir bayrak değilse, enumu birden farklı enumlara bölmeyi düşünün.
Şimdi, örneğin (biraz değişmiş):
public enum Role
{
User,
Admin
}
public class User
{
public List<Role> Roles { get; set; }
}
Bu tam davanın şu şekilde modellenmesi gerektiğini düşünüyorum (not: gerçekten genişletilemez!):
public class User
{
public bool IsAdmin { get; set; }
}
Başka bir deyişle - zımni olduğu, yani User
a User
, ek bilgi onun bir olup olmadığıdır Admin
.
Eğer özel değildir birden çok rol var alırsanız (örneğin User
olabilir Admin
, Moderator
, VIP
, ... aynı zamanda), bu da bayraklar enum kullanmak için iyi bir zaman ya da bir abtract taban sınıfı veya arabirim olurdu.
Bir sınıfı temsil etmek için bir sınıf kullanmak, verilen bir işlemi yapıp yapamayacağına karar verme sorumluluğunun olabileceği Role
durumlarda sorumlulukların daha iyi ayrılmasına yol açar Role
.
Enum ile, tüm roller için mantığı tek bir yerde bulundurmanız gerekir. OO'nun amacını yitirir ve sizi zorunlu hale getirir.
Bir Moderator
düzenleme haklarına sahip olduğunu ve Admin
hem düzenleme hem de silme haklarına sahip olduğunu hayal edin .
Enum yaklaşımı ( Permissions
rolleri ve izinleri karıştırmamak için çağrılır ):
[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;
}
}
Sınıf yaklaşımı (bu mükemmel olmaktan uzak, ideal olarak, IAction
bir ziyaretçi düzeninde yer almak istersiniz, ancak bu yazı zaten çok büyük ...):
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));
}
}
Bir enum kullanmak olsa da (örneğin performans) kabul edilebilir bir yaklaşım olabilir. Buradaki karar, modellenen sistemin gereksinimlerine bağlıdır.