有人一直在谈论Enums一般违反清洁守则原则,所以我正在寻找人们最喜欢的Enum反模式和替代解决方案。
例如,我见过这样的代码:
switch(enumValue) {
case myEnum.Value1:
// ...
break;
case myEnum.Value2:
// ...
break;
}
它比带有魔术字符串的switch语句更好一步,但这可能是用工厂,容器或其他模式更好地解决的。
甚至是这样的老派代码:
if(enumValue == myEnum.Value1) {
// ...
} else if (enumValue == myEnum.Value2) {
// ...
}
您在枚举方面遇到了哪些其他反模式和更好的实施?
答案 0 :(得分:11)
我认为Enums非常有用。我为Enum写了一些扩展,为其使用增加了更多的价值
首先,有描述扩展方法
public static class EnumExtensions
{
public static string Description(this Enum value)
{
var entries = value.ToString().Split(ENUM_SEPERATOR_CHARACTER);
var description = new string[entries.Length];
for (var i = 0; i < entries.Length; i++)
{
var fieldInfo = value.GetType().GetField(entries[i].Trim());
var attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
description[i] = (attributes.Length > 0) ? attributes[0].Description : entries[i].Trim();
}
return String.Join(", ", description);
}
private const char ENUM_SEPERATOR_CHARACTER = ',';
}
这将允许我像这样定义en enum:
public enum MeasurementUnitType
{
[Description("px")]
Pixels = 0,
[Description("em")]
Em = 1,
[Description("%")]
Percent = 2,
[Description("pt")]
Points = 3
}
通过这样做获得标签:var myLabel = rectangle.widthunit.Description()
(不需要switch
语句。)
如果rectangle.widthunit = MeasurementUnitType.Pixels
,它将返回“px”,如果rectangle.widthunit = MeasurementUnitType.Pixels | MeasurementUnitType.Em
,它将返回“px,em”。
然后,有一个
public static IEnumerable<int> GetIntBasedEnumMembers(Type @enum)
{
foreach (FieldInfo fi in @enum.GetFields(BindingFlags.Public | BindingFlags.Static))
yield return (int)fi.GetRawConstantValue();
}
这将让我遍历任何基于int的值的枚举,并返回int值。
我发现这些在一个已经很有用的概念中非常有用。
答案 1 :(得分:1)
我认为将两个切换语句作为非OO设计as explained further in this answer的症状。
答案 2 :(得分:1)
这不是答案,而是对Enum反模式列表的贡献。
在今天早上的代码审核期间,我遇到了类似于以下内容的案例,所有案例都在同一个类中。
两种情况:
...
public enum ListEnum
{
CategoryOne,
CategoryTwo,
CategoryThree,
CategoryFour
}
public class UIELementType
{
public const string FactoryDomain = "FactoryDomain";
public const string Attributes = "Attributes";
}
答案 3 :(得分:0)
使用非反模式的枚举。在一些关于重构的书中,这段代码用于演示如何用多态替换它。当你在代码中过度使用枚举时就可以了。
答案 4 :(得分:0)
这一切都取决于您对枚举的处理方式。
如果您试图阻止开发人员将魔术数字传递到您的操作中,并且希望保持数据库的数据参照完整性不变,那么,是的!使用T4-Templates(使用ORM)转到MeasurementUnitTypes表,并生成一个ID,Name和Description列与该枚举的int,Enum_Name和Description属性匹配的枚举(对枚举@danijels的其他字段\数据的不错方法)如上所述。如果将新的Measurement Type添加到MeasurementUnitTypes表中,则可以右键单击并运行T4-Template,并为表中添加的该新行生成枚举代码。我不喜欢应用程序中未链接到数据库的硬编码数据,因此提到了T4-Template方法。否则它是不可扩展的……如果某个其他外部系统想要检索我们的系统中使用的度量标准,那么它将在系统中进行硬编码,并且您将无法通过服务将其公开给客户端。那留在那儿。
如果目的与数据无关,并且您已将逻辑分配给特定的枚举,则为否!这违反了SOLID(开放关闭原则),就像您在应用程序中的某个位置应用开关或一系列If来操作每个枚举的逻辑一样,如果您这样做真的很糟糕,那么这些开关或If遍及整个过程....祝您好运,并添加了一个新的枚举...因此,根据SOLID原则,它不能为扩展而开放,而不能为修改而封闭,因为您需要修改现有代码。
如果您选择的是2,那么我建议然后使用@danijels注释中的示例将您的枚举替换为以下内容:
public interface IMeasurementUnitType
{
int ID { get; }
string Description { get; }
// Just added to simulate a action needed in the system
string GetPrintMessage(int size);
}
上面的代码定义了每个度量应遵循的接口(代码协定)。现在让我们定义百分比和像素测量:
public class PixelsMeasurementUnitType : IMeasurementUnitType
{
public int ID => 1;
public string Description => "Pixel";
public string GetPrintMessage(int size)
{
return $"This is a {Description} Measurement that is equal to {size} pixels of the total screen size";
}
}
public class PercentMeasurementUnitType : IMeasurementUnitType
{
public int ID => 2;
public string Description => "Persentage";
public string GetPrintMessage(int size)
{
return $"This is a {Description} Measurement that is equal to {size} persent of total screen size (100)";
}
}
所以我们定义了两种类型,我们将在代码中使用它们,如下所示:
var listOfMeasurmentTypes = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => typeof(IMeasurementUnitType).IsAssignableFrom(p)
&& !p.IsInterface)
.ToList();
在这里,我们获取所有扩展IMeasurementUnitType接口的类型,而不是接口本身。现在,我们可以使用Activator创建类的实例来填充我们的UI控件:
public IEnumerable<IMeasurementUnitType> GetInstantiatedClassesFromTypes(List<Type> types)
{
foreach (var type in types)
{
yield return (IMeasurementUnitType)Activator.CreateInstance(type);
}
}
您可以将上面的代码更改为任何类型的通用代码,并且现在发生寿命,并且客户将新的称为Point的测量单位类型指定为新要求,我不需要更改任何代码,只需添加新代码类型(扩展代码,不要修改)。新类型将自动在应用程序中显示。
public class PointMeasurementUnitType : IMeasurementUnitType
{
public int ID => 3;
public string Description => "Point";
public string GetPrintMessage(int size)
{
return $"This is a {Description} Measurement that is equal to {size} points of total screen size";
}
}
一个好主意是在启动应用程序时尝试缓存您的类型以提高性能,或者尝试使用您选择的DI容器。
另外,有人可能会争辩说,您需要在应用程序中的某个地方区分类型,我同意,但是您希望苹果与苹果并驾齐驱。因此,请尽量尝试应用与该类型相同的原理。如果在某种类型的图形处理器(例如)类中使用此类型,请使用IGraphicsProcessor并让您的具体类区分这些类型,例如PersentageAndPixelGraphicsProcessor(从IGraphicsProcessor扩展而来),或者仅区分一种类型,则称为PersentageGraphicsProcessor。
对不起,HUGE SA很抱歉,但是我真的很喜欢enum,但是当您尝试使用enum分离逻辑时,我觉得它是很强的反模式。
欢迎评论