我的域模型中有几个不同的实体(动物物种,比方说),每个实体都有一些属性.实体是只读的(它们在应用程序生命周期内不会改变状态)并且它们具有相同的行为(仅由属性值不同).
@H_502_2@如何在代码中实现这样的实体?@H_403_3@
@H_502_2@不成功的尝试:@H_403_3@
@H_502_2@枚举@H_403_3@
@H_502_2@我尝试了这样的枚举:@H_403_3@
enum Animals { Frog,Duck,Otter,Fish }@H_502_2@其他代码段将打开枚举.然而,这导致了丑陋的切换代码,分散了逻辑和problems with comboboxes.没有很好的方法来列出所有可能的动物.序列化虽然很好.@H_403_3@ @H_502_2@子类@H_403_3@ @H_502_2@我还想过每个动物类型在哪里是公共基础抽象类的子类.但是,对于所有动物来说,Swim()的实现是相同的,因此它没有多大意义,可串行化现在是一个大问题.由于我们代表一种动物类型(物种,如果你愿意),每个应用程序应该有一个子类的实例,当我们使用序列化时,这很难维护.@H_403_3@
public abstract class AnimalBase { string Name { get; set; } // user-readable double Weight { get; set; } Habitat Habitat { get; set; } public void Swim(); { /* swim implementation; the same for all animals but depends uses the value of Weight */ } } public class Otter: AnimalBase{ public Otter() { Name = "Otter"; Weight = 10; Habitat = "North America"; } } // ... and so on@H_502_2@简直太糟糕了.@H_403_3@ @H_502_2@静态字段@H_403_3@ @H_502_2@This blog post给了我一个解决方案的想法,其中每个选项都是类型中静态定义的字段,如下所示:@H_403_3@
public class Animal { public static readonly Animal Otter = new Animal { Name="Otter",Weight = 10,Habitat = "North America"} // the rest of the animals... public string Name { get; set; } // user-readable public double Weight { get; set; } public Habitat Habitat { get; set; } public void Swim();@H_502_2@}@H_403_3@ @H_502_2@那会很棒:你可以像枚举一样使用它(AnimalType = Animal.Otter),你可以轻松添加所有已定义动物的静态列表,你有一个合理的地方可以实现Swim().通过使财产制定者受到保护可以实现不变性.但是存在一个主要问题:它打破了可串行化.序列化的Animal必须保存它的所有属性,并且在反序列化时它会创建一个Animal的新实例,这是我想要避免的.@H_403_3@ @H_502_2@是否有一种简单的方法可以使第三次尝试工作?有关实施此类模型的更多建议吗?@H_403_3@
解决方法
如果您遇到序列化问题,可以随时将应用程序代码与序列化代码分开.也就是说,放置转换为序列化状态的转换类.序列化实例可以暴露任何所需的空构造函数和属性,它们唯一的工作是序列化状态.同时,您的应用程序逻辑使用不可序列化的不可变对象.这样,您就不会将序列化问题与逻辑问题混合在一起,因为您发现它会带来许多不利因素.
@H_502_2@编辑:这是一些示例代码:@H_403_3@
public class Animal { public string Name { get; private set; } public double Weight { get; private set; } public Habitat Habitat { get; private set; } internal Animal(string name,double weight,Habitat habitat) { this.Name = name; this.Weight = weight; this.Habitat = habitat; } public void Swim(); } public class SerializableAnimal { public string Name { get; set; } public double Weight { get; set; } public SerializableHabitat Habitat { get; set; } //assuming the "Habitat" class is also immutable } public static class AnimalSerializer { public static SerializableAnimal CreateSerializable(Animal animal) { return new SerializableAnimal {Name=animal.Name,Weight=animal.Weight,Habitat=HabitatSerializer.CreateSerializable(animal.Habitat)}; } public static Animal CreateFromSerialized(SerializableAnimal serialized) { return new Animal(serialized.Name,serialized.Weight,HabitatSerializer.CreateFromSerialized(serialized.Habitat)); } //or if you're using your "Static fields" design,you can switch/case on the name public static Animal CreateFromSerialized(SerializableAnimal serialized) { switch (serialized.Name) { case "Otter" : return Animal.Otter } return null; //or throw exception } }@H_502_2@那么序列化的应用程序逻辑可能如下所示:@H_403_3@
Animal myAnimal = new Animal("Otter",10,"North America"); Animal myOtherAnimal = Animal.Duck; //static fields example SerializableAnimal serializable = AnimalSerializer.CreateSerializable(myAnimal); string xml = XmlSerialize(serializable); SerializableAnimal deserialized = XmlDeserializer<SerializableAnimal>(xml); Animal myAnimal = AnimalSerializer.CreateFromSerialized(deserialized);@H_502_2@重申一下,SerializableAnimal类和用法仅用于需要序列化/反序列化的应用程序的最后一层.其他一切都适用于你的不可变的Animal类.@H_403_3@ @H_502_2@EDITx2:此托管分离的另一个主要好处是您可以处理代码中的旧版更改.例如,你有一个Fish类型,它相当广泛.也许你以后将它分成鲨鱼和金鱼,并决定你所有的旧鱼类应该被认为是金鱼.通过这种序列化的分离,您现在可以检查任何旧Fish并将它们转换为Goldfish,而直接序列化将导致异常,因为Fish不再存在.@H_403_3@