这个问题是我先前回答的问题的副产品。当成员与另一个类的公共变量相同时使用Enum。在实现所提供的答案时,我面临着另一个设计问题。
我正在写一个程序来模拟进化。我有一个enum,它存储一个蛋白质可以编码的可能属性的列表。蛋白质是由一系列氨基酸(即"W“、"A”等)现在,我有一个字典,它将一个属性(enum成员)与一个字符链接起来。同样,我对蛋白质类型也有相同的设置。
public class Protein {
Property property;
float value;
Type type;
enum Property {
SIZE,
ID,
}
enum Type {
STRUCTURAL,
NEURAL,
}
static Dictionary<string, Property> aminoToProperty
= new Dictionary<string, Property> {
{"F", Property.SIZE},
{"K", Property.ID},
};
static Dictionary<string, Type> aminoToType
= new Dictionary<string, Property> {
{"A", Type.STRUCTURAL},
{"B", Type.NEURAL},
};
}从另一个类别,我正在创造蛋白质的基础上,长串的字符,然后结合起来,形成结构。几种蛋白质可以加起来,为这种结构创造各种性质和价值。
Structure structure = new Structure();
Protein protein = new Protein (aminos) // didn't show this method for simplicity
switch (protein.property) {
case Protein.Property.SIZE:
structure.size += protein.value;
break;
ONE FOR EVERY PROPERTY...
}
SAME THING FOR EVERY TYPE...我的问题:
1)当我想要在混合中添加一个新属性时,首先需要在枚举中添加一个条目,然后再添加到字典中。这让我觉得我违反了某种最佳做法。
( 2)我需要为每一项财产和类型保留一个案件,这是否不可避免?
( 3)我是否可以保证,在这两本字典中,我不会意外地使用同一封信两次?有两本字典有意义吗?
4)也许我用完全错误的方式处理这个问题。
注意:在较旧的链接问题中,我将值与枚举一起存储。但是在这里,我需要有多个具有不同值的实例,所以我认为这个解决方案不适用。
抱歉,如果这是混乱,但我真的困在如何最好的设计,使它成为未来的证据。我是用c#编写的,但我认为这个问题适用于大多数面向对象编程语言。
发布于 2016-05-06 22:06:06
首先,如果我不熟悉问题领域,请原谅我;我的例子可能反映了这一点。
我认为你的直觉很好地服务于你,因为枚举解决方案似乎违反了开放/封闭原则。
另一种解决这个问题的方法是,在添加新的蛋白质属性和类型时,不需要触及多个位置,而是使接受结构并对其进行操作的ProteinType和ProteinProperty抽象基类。
然后,蛋白质可以聚合这两种类型,接受一个结构,并将结构委托给ProteinType和ProteinProperty实例,以便大多数派生类型可以修改该结构。
另一件可以自动化的事情是使用反射向ProteinType和ProteinProperty查找一个氨基酸。
这一切看起来都太过分了,但最终的结果是,您现在可以自由地单独定义ProteinType和ProteinProperty类,而不必担心修改代码的其他区域。
下面是一个例子:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
static void Main()
{
var structure = new Structure();
var protein = new Protein("F", "B", 0.5F);
protein.ApplyToStructure(structure);
}
}
public class Protein
{
private readonly float _value;
private readonly ProteinProperty _proteinProperty;
private readonly ProteinType _proteinType;
public Protein(string propertyAmino, string typeAmino, float value)
{
_proteinProperty = ProteinProperty.FromAmino(propertyAmino);
_proteinType = ProteinType.FromAmino(typeAmino);
_value = value;
}
public void ApplyToStructure(Structure structure)
{
_proteinProperty.ApplyToStructure(structure, _value);
_proteinType.ApplyToStructure(structure, _value);
}
}
public class Structure
{
public float Size { get; set; }
public float Id { get; set; }
}
public abstract class ProteinType
{
private static readonly Dictionary<string, ProteinType> AllProteinTypes = typeof (ProteinType)
.GetNestedTypes(BindingFlags.Instance | BindingFlags.NonPublic)
.Where(nestedType => typeof (ProteinType).IsAssignableFrom(nestedType))
.Select(nestedType => (ProteinType) Activator.CreateInstance(nestedType))
.ToDictionary(proteinType => proteinType.Amino);
public static ProteinType FromAmino(string amino)
{
ProteinType proteinType;
if (!AllProteinTypes.TryGetValue(amino, out proteinType))
{
throw new ArgumentException("Invalid amino");
}
return proteinType;
}
public abstract string Amino { get; }
public abstract void ApplyToStructure(Structure structure, float value);
private sealed class StructuralProteinType : ProteinType
{
public override string Amino { get; } = "A";
public override void ApplyToStructure(Structure structure, float value)
{
// do what you need to do to Structure
}
}
private sealed class NeuralProteinType : ProteinType
{
public override string Amino { get; } = "B";
public override void ApplyToStructure(Structure structure, float value)
{
// do what you need to do to Structure
}
}
}
public abstract class ProteinProperty
{
private static readonly Dictionary<string, ProteinProperty> AllProteinProperties = typeof(ProteinProperty)
.GetNestedTypes(BindingFlags.Instance | BindingFlags.NonPublic)
.Where(nestedType => typeof(ProteinProperty).IsAssignableFrom(nestedType))
.Select(nestedType => (ProteinProperty)Activator.CreateInstance(nestedType))
.ToDictionary(proteinProperty => proteinProperty.Amino);
public static ProteinProperty FromAmino(string amino)
{
ProteinProperty proteinProperty;
if (!AllProteinProperties.TryGetValue(amino, out proteinProperty))
{
throw new ArgumentException("Invalid amino");
}
return proteinProperty;
}
public abstract string Amino { get; }
public abstract void ApplyToStructure(Structure structure, float value);
private sealed class SizeProteinProperty : ProteinProperty
{
public override string Amino { get; } = "F";
public override void ApplyToStructure(Structure structure, float value)
{
structure.Size += value;
}
}
private sealed class IdProteinProperty : ProteinProperty
{
public override string Amino { get; } = "K";
public override void ApplyToStructure(Structure structure, float value)
{
// do what you need to do to Structure
}
}
}
}编辑
在再次阅读您的问题并试图将我的头绕到问题域之后,我想知道ProteinProperty和ProteinType...seems之间是否有区别--它们只是对结构有影响的一个氨基酸字符的表示。你还指出,氨基是一串字符。
如果是这样的话,我将把ProteinProperty和ProtienType替换为一个单独的基类:氨基。此外,您还可以让阿米诺基类的ApplyToStructure()方法接受整个蛋白质实例,以便您可以在派生的Amino类中访问它上的其他内容。
最后,您可以基于一个氨基酸字符串创建一个an实例集合,并将它们应用到循环中的结构中。
下面是更新的示例:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
static void Main()
{
var structure = new Structure();
var protein = new Protein(0.5F, "FKABAKB");
protein.ApplyToStructure(structure);
}
}
public class Protein
{
private readonly ReadOnlyCollection<Amino> _aminos;
public Protein(float value, string aminos)
{
Value = value;
_aminos = aminos
.Select(Amino.FromAminoCharater)
.ToList()
.AsReadOnly();
}
public float Value { get; }
public void ApplyToStructure(Structure structure)
{
foreach (var amino in _aminos)
{
amino.ApplyToStructure(structure, this);
}
}
}
public class Structure
{
public float Size { get; set; }
public float Id { get; set; }
}
public abstract class Amino
{
private static readonly Dictionary<char, Amino> AllAminos = typeof(Amino)
.GetNestedTypes(BindingFlags.Instance | BindingFlags.NonPublic)
.Where(nestedType => typeof(Amino).IsAssignableFrom(nestedType))
.Select(nestedType => (Amino)Activator.CreateInstance(nestedType))
.ToDictionary(proteinProperty => proteinProperty.AminoCharacter);
public static Amino FromAminoCharater(char aminoCharacter)
{
Amino amino;
if (!AllAminos.TryGetValue(aminoCharacter, out amino))
{
throw new ArgumentException("Invalid amino");
}
return amino;
}
public abstract char AminoCharacter { get; }
public abstract void ApplyToStructure(Structure structure, Protein protein);
private sealed class SizeProperty : Amino
{
public override char AminoCharacter { get; } = 'F';
public override void ApplyToStructure(Structure structure, Protein protein)
{
structure.Size += protein.Value;
}
}
private sealed class IdProperty : Amino
{
public override char AminoCharacter { get; } = 'K';
public override void ApplyToStructure(Structure structure, Protein protein)
{
// do what you need to do to Structure
}
}
private sealed class StructuralType : Amino
{
public override char AminoCharacter { get; } = 'A';
public override void ApplyToStructure(Structure structure, Protein protein)
{
// do what you need to do to Structure
}
}
private sealed class NeuralType : Amino
{
public override char AminoCharacter { get; } = 'B';
public override void ApplyToStructure(Structure structure, Protein protein)
{
// do what you need to do to Structure
}
}
}
}还要注意,这解决了不小心引入重复项的问题。如果你用另一个字母定义一个阿米诺类,你的应用程序会立即崩溃和烧毁,而不是有一个微妙的bug。
发布于 2016-05-06 10:31:33
我能理解你的问题,因为我以前去过那里。请看枚举的问题是它不能扩展,因为没有继承机制可以这样做。因此,添加新的属性或类型意味着您修改枚举,从而引发兼容性破坏问题,并需要部署新的exe或DLL。当选择从一开始就处于静态状态时,Enum是好的。例如
方向:左,上,右,下。
方向:水平,垂直。
现在,您的情况是典型的需要扩展枚举,这是不可完成的,所以,您将如何编写一些事情,这样做。简单的答案是转储枚举。将“字段”属性和“类型”更改为字符串而不是枚举。现在提供解析属性的虚拟方法或构建结构的Type。下面是psuedo代码:
public class Protein {
string property;
float value;
string type;
static Dictionary<string, string> aminoToProperty= new Dictionary<string, string> {
{"F", "SIZE"},
{"K", "ID"},
};
static Dictionary<string, Type> aminoToType = new Dictionary<string, string> {
{"A", "STRUCTURAL"},
{"B", "NEURAL"},
};现在切换语句中的更改:
protected virtual void assignValues(Protein Protein){
switch (protein.property.ToUpper()) {
case "SIZE":
structure.size += protein.value;
break;
//ONE FOR EVERY PROPERTY...
}
//SAME THING FOR EVERY TYPE...
}现在,这里的关键是"assignValues“声明的虚拟方法,这样您就可以覆盖它,以适应新的属性或类型。即使在程序集之外,设计他的类(从您的蛋白质类继承)的其他人也可以添加属性或类型,并且可以成功地管理它,而不需要太多麻烦。
我再次强调,如果你确信它包含了所有可能的选择而不需要任何改变,就使用枚举。在开始的时候,你已经得到了你所需要的一切选择,那么枚举就是你最好的伙伴。
https://stackoverflow.com/questions/36881443
复制相似问题