首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >设计问题:需要枚举成员的实例作为(属性,值)对

设计问题:需要枚举成员的实例作为(属性,值)对
EN

Stack Overflow用户
提问于 2016-04-27 05:59:27
回答 2查看 408关注 0票数 1

这个问题是我先前回答的问题的副产品。当成员与另一个类的公共变量相同时使用Enum。在实现所提供的答案时,我面临着另一个设计问题。

我正在写一个程序来模拟进化。我有一个enum,它存储一个蛋白质可以编码的可能属性的列表。蛋白质是由一系列氨基酸(即"W“、"A”等)现在,我有一个字典,它将一个属性(enum成员)与一个字符链接起来。同样,我对蛋白质类型也有相同的设置。

代码语言:javascript
复制
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},
    };


}

从另一个类别,我正在创造蛋白质的基础上,长串的字符,然后结合起来,形成结构。几种蛋白质可以加起来,为这种结构创造各种性质和价值。

代码语言:javascript
复制
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#编写的,但我认为这个问题适用于大多数面向对象编程语言。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-05-06 22:06:06

首先,如果我不熟悉问题领域,请原谅我;我的例子可能反映了这一点。

我认为你的直觉很好地服务于你,因为枚举解决方案似乎违反了开放/封闭原则

另一种解决这个问题的方法是,在添加新的蛋白质属性和类型时,不需要触及多个位置,而是使接受结构并对其进行操作的ProteinType和ProteinProperty抽象基类。

然后,蛋白质可以聚合这两种类型,接受一个结构,并将结构委托给ProteinType和ProteinProperty实例,以便大多数派生类型可以修改该结构。

另一件可以自动化的事情是使用反射向ProteinType和ProteinProperty查找一个氨基酸。

这一切看起来都太过分了,但最终的结果是,您现在可以自由地单独定义ProteinType和ProteinProperty类,而不必担心修改代码的其他区域。

下面是一个例子:

代码语言:javascript
复制
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实例集合,并将它们应用到循环中的结构中。

下面是更新的示例:

代码语言:javascript
复制
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。

票数 2
EN

Stack Overflow用户

发布于 2016-05-06 10:31:33

我能理解你的问题,因为我以前去过那里。请看枚举的问题是它不能扩展,因为没有继承机制可以这样做。因此,添加新的属性或类型意味着您修改枚举,从而引发兼容性破坏问题,并需要部署新的exe或DLL。当选择从一开始就处于静态状态时,Enum是好的。例如

方向:左,上,右,下。

方向:水平,垂直。

现在,您的情况是典型的需要扩展枚举,这是不可完成的,所以,您将如何编写一些事情,这样做。简单的答案是转储枚举。将“字段”属性和“类型”更改为字符串而不是枚举。现在提供解析属性的虚拟方法或构建结构的Type。下面是psuedo代码:

代码语言:javascript
复制
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"},
    };

现在切换语句中的更改:

代码语言:javascript
复制
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“声明的虚拟方法,这样您就可以覆盖它,以适应新的属性或类型。即使在程序集之外,设计他的类(从您的蛋白质类继承)的其他人也可以添加属性或类型,并且可以成功地管理它,而不需要太多麻烦。

我再次强调,如果你确信它包含了所有可能的选择而不需要任何改变,就使用枚举。在开始的时候,你已经得到了你所需要的一切选择,那么枚举就是你最好的伙伴。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36881443

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档