我在工作中讨论过“域模型中的继承使开发人员生活变得复杂”。我是OO程序员,所以我开始寻找在域模型中具有继承性的参数,这样可以简化开发人员的生活,而不是让交换机到处都是。
我希望看到的是:
class Animal {
}
class Cat : Animal {
}
class Dog : Animal {
}
另一位同事说的是:
public enum AnimalType {
Unknown,
Cat,
Dog
}
public class Animal {
public AnimalType Type { get; set; }
}
我如何说服他(链接 WELCOME )一个类层次结构比在这种情况下拥有枚举属性更好?
谢谢!
答案 0 :(得分:25)
以下是我的理由:
如果角色/类型永远不会更改,则仅使用继承。 例如
将继承用于:
Fireman< - 员工< - 人是错的。
一旦消防员弗雷迪改变工作或失业,你必须杀死他并重新创造一个新类型的新物体,并附上所有旧关系。因此,上述问题的天真解决方案是将JobTitle枚举属性赋予person类。 在某些情况下,这可能就足够了,例如如果您不需要与角色/类型相关的非常复杂的行为。
更正确的方法是为person类提供一个角色列表。 每个角色都代表着一个具有时间跨度的就业。
e.g。
freddy.Roles.Add(new Employement( employmentDate, jobTitle ));
或者如果那是过度杀伤:
freddy.CurrentEmployment = new Employement( employmentDate, jobTitle );
这样,弗雷迪可以成为一名开发人员而不必先杀死他。
但是,如果您应该为作业标题使用枚举或类型层次结构,我的所有漫游仍然没有回答。
在纯粹的mem OO中我会说在这里使用继承来更正确。
但是,如果您正在进行O / R映射,那么如果映射器尝试将每个子类型映射到新表,则可能会在后台使用一些过于复杂的数据模型。 所以在这种情况下,如果没有与类型相关的真实/复杂行为,我经常会采用枚举方法。 如果使用量有限并且使事情更容易或更简单,我可以使用“if type == JobTitles.Fireman ......”。
e.g。 .NET的Entity Framework 4设计器只能将每个子类型映射到新表。当你查询数据库没有任何实际好处时,你可能会得到一个丑陋的模型或很多连接。
但是,如果类型/角色是静态的,我会使用继承。 例如对于产品。
你可能有CD< - Product and Book< - Product。 继承在这里获胜,因为在这种情况下,您很可能具有与类型相关联的不同状态。 CD可能具有多个轨道属性,而一本书可能具有多个页面属性。
总之,这取决于; - )
此外,在一天结束时,您很可能会以任何方式结束许多切换语句。 假设您要编辑“产品”,即使您使用继承,您可能会有这样的代码:
if(产品是Book) Response.Redicted(“〜/ EditBook.aspx?id”+ product.id);
因为在实体类中对编辑书网址进行编码会非常难看,因为它会迫使您的业务人员了解您的网站结构等。
答案 1 :(得分:7)
以下情况时,枚举很好:
如果你能用一个数字来解决你的问题,那么枚举可能是一个很好的选择,而且更安全。如果您需要比上述更多的灵活性,那么枚举可能不正确的答案。使用多态类,您可以:
静态确保处理所有特定于类型的行为。例如,如果您需要所有动物都能够Bark()
,那么使用抽象Animal
方法生成Bark()
类将让编译器检查每个子类是否实现它。如果您使用枚举和大switch
,则无法确保您已处理完所有案例。
您可以添加新案例(示例中的动物类型)。这可以跨源文件完成,甚至可以跨包边界完成。有了枚举,一旦你宣布它,就会被冻结。开放式扩展是OOP的主要优势之一。
重要的是要注意你的同事的例子并不是与你的直接对立。如果他想让动物的类型成为暴露的属性(这对某些事情很有用),你仍然可以在不使用枚举的情况下使用type object pattern:
public abstract class AnimalType {
public static AnimalType Unknown { get; private set; }
public static AnimalType Cat { get; private set; }
public static AnimalType Dog { get; private set; }
static AnimalType() {
Unknown = new AnimalType("Unknown");
Cat = new AnimalType("Cat");
Dog = new AnimalType("Dog");
}
}
public class Animal {
public AnimalType Type { get; set; }
}
这为您提供了枚举的便利:您可以AnimalType.Cat
,您可以获得动物的类型。但它也为您提供了类的灵活性:您可以向AnimalType
添加字段以存储每种类型的附加数据,添加虚拟方法等。更重要的是,您可以通过创建{的新实例来定义新的动物类型{1}}。
答案 2 :(得分:7)
我建议你重新考虑:在anemic domain model(根据上面的评论),猫的行为与狗的行为不同,所以没有多态性。动物的类型真的只是一个属性。很难看出继承在那里购买的东西。
答案 3 :(得分:5)
拥有枚举就像为所有Open/Closed Principle is for suckers
人举办派对一样。
它真的邀请您检查动物是否属于某种类型,然后为每种类型应用自定义逻辑。这可能会导致可怕的代码,这使得在您的系统上继续构建变得非常困难。
答案 4 :(得分:1)
最重要的是,OOPS意味着对现实进行建模。继承让你有机会说猫是一种动物。动物不应该知道它的猫是否现在喊它然后决定它是喵而不是树皮,封装在那里被击败。现在你不需要做更少的代码如果你说的其他的话。
答案 5 :(得分:1)
两种解决方案都是正确的。 你应该看看哪种技术更适合你的问题。
如果您的程序使用了很少的不同对象,并且没有添加新类,那么最好继续使用枚举。
但是如果你的程序使用了很多不同的对象(不同的类),并且可能会添加新的类,将来更好的尝试继承方式。