域模型中的继承vs枚举属性

时间:2010-11-23 08:48:55

标签: oop inheritance enums domain-driven-design domain-model

我在工作中讨论过“域模型中的继承使开发人员生活变得复杂”。我是OO程序员,所以我开始寻找在域模型中具有继承性的参数,这样可以简化开发人员的生活,而不是让交换机到处都是。

我希望看到的是:

class Animal {

}

class Cat : Animal {

}

class Dog : Animal {

}

另一位同事说的是:

public enum AnimalType {
    Unknown,
    Cat,
    Dog
}

public class Animal {

    public AnimalType Type { get; set; }

}

我如何说服他(链接 WELCOME )一个类层次结构比在这种情况下拥有枚举属性更好?

谢谢!

6 个答案:

答案 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)

以下情况时,枚举很好:

  1. 这组值是固定的,从不或很少更改。
  2. 您希望能够表示值的组合(即组合标志)。
  3. 您不需要为每个值附加其他状态。 (Java没有此限制。)
  4. 如果你能用一个数字来解决你的问题,那么枚举可能是一个很好的选择,而且更安全。如果您需要比上述更多的灵活性,那么枚举可能正确的答案。使用多态类,您可以:

    1. 静态确保处理所有特定于类型的行为。例如,如果您需要所有动物都能够Bark(),那么使用抽象Animal方法生成Bark()类将让编译器检查每个子类是否实现它。如果您使用枚举和大switch,则无法确保您已处理完所有案例。

    2. 您可以添加新案例(示例中的动物类型)。这可以跨源文件完成,甚至可以跨包边界完成。有了枚举,一旦你宣布它,就会被冻结。开放式扩展是OOP的主要优势之一。

    3. 重要的是要注意你的同事的例子并不是与你的直接对立。如果他想让动物的类型成为暴露的属性(这对某些事情很有用),你仍然可以在不使用枚举的情况下使用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)

两种解决方案都是正确的。 你应该看看哪种技术更适合你的问题。

如果您的程序使用了很少的不同对象,并且没有添加新类,那么最好继续使用枚举。

但是如果你的程序使用了很多不同的对象(不同的类),并且可能会添加新的类,将来更好的尝试继承方式。