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

Dan*_*rin 36 oop inheritance enums domain-driven-design domain-model

我在工作中讨论过"域模型中的继承使开发人员生活变得复杂".我是OO程序员,所以我开始寻找在域模型中具有继承性的论据,这样可以简化开发人员的生活,而不是让交换机遍布整个地方.

我想看到的是:

class Animal {

}

class Cat : Animal {

}

class Dog : Animal {

}
Run Code Online (Sandbox Code Playgroud)

另一位同事说的是:

public enum AnimalType {
    Unknown,
    Cat,
    Dog
}

public class Animal {

    public AnimalType Type { get; set; }

}
Run Code Online (Sandbox Code Playgroud)

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

谢谢!

Rog*_*son 26

以下是我的理由:

如果角色/类型永远不会更改,则仅使用继承.例如

使用继承的东西,如:

消防员< - 员工< - 人是错的.

一旦消防员弗雷迪改变工作或失业,你必须杀死他并重新创造一个新类型的新物体,并附上所有旧关系.

因此,上述问题的天真解决方案是将PersonTitle枚举属性赋予person类.在某些情况下,这可能就足够了,例如,如果您不需要与角色/类型相关的非常复杂的行为.

更正确的方法是为person类提供一个角色列表.每个角色代表例如具有时间跨度的就业.

例如

freddy.Roles.Add(new Employement( employmentDate, jobTitle ));
Run Code Online (Sandbox Code Playgroud)

或者如果那是过度的话:

freddy.CurrentEmployment = new Employement( employmentDate, jobTitle );
Run Code Online (Sandbox Code Playgroud)

这样,弗雷迪可以成为一名开发人员而不必先杀死他.

但是,如果你应该为作业标题使用枚举或类型层次结构,我的所有rambling仍然没有回答.

在纯粹的mem OO中,我会说在这里使用继承作为工作标题是更正确的.

但是,如果您正在进行O/R映射,那么如果映射器尝试将每个子类型映射到新表,则可能会在后台使用一些过于复杂的数据模型.所以在这种情况下,如果没有与类型相关的真实/复杂行为,我经常会采用枚举方法.如果使用量有限,我可以使用"if type == JobTitles.Fireman ......",这样可以使事情更轻松或更简单.

例如,.NET的Entity Framework 4 designer只能将每个子类型映射到一个新表.当你查询数据库没有任何实际好处时,你可能会得到一个丑陋的模型或很多连接.

但是,如果类型/角色是静态的,我会使用继承.例如对于产品.

你可能有CD < - 产品和书< - 产品.继承在这里获胜,因为在这种情况下,您很可能具有与类型相关联的不同状态.CD可能具有多个轨道属性,而一本书可能具有多个页面属性.

所以总之,这取决于;-)

此外,在一天结束时,您很可能会以任何方式结束许多切换语句.假设您要编辑"产品",即使您使用继承,您可能会有这样的代码:

if(product is Book)Response.Redicted("〜/ EditBook.aspx?id"+ product.id);

因为在实体类中对编辑书网址进行编码会非常难看,因为它会迫使您的业务人员了解您的网站结构等.


mun*_*ent 8

在以下情况下,枚举很好

  1. 这组值是固定的,从不或很少变化.
  2. 您希望能够表示值的并集(即组合标志).
  3. 您不需要将其他状态附加到每个值.(Java没有这个限制.)

如果你能用一个数字来解决你的问题,那么枚举可能是一个很好的选择,而且更安全.如果您需要比上述更多的灵活性,那么枚举可能不是正确的答案.使用多态类,您可以:

  1. 静态地确保处理所有类型特定的行为.例如,如果您需要所有动物,则使用抽象方法Bark()创建AnimalBark()将让编译器检查每个子类是否实现它.如果你使用enum和big switch,它将不能确保你处理每个案例.

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

重要的是要注意你的同事的例子并不是与你的直接对立.如果他想让一个动物的类型成为一个暴露的属性(这对某些东西很有用),你仍然可以在不使用枚举的情况下使用类型对象模式:

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; }
}
Run Code Online (Sandbox Code Playgroud)

这为你提供了enum的便利:你可以做AnimalType.Cat,你可以得到动物的类型.但它也为您提供了类的灵活性:您可以添加字段以AnimalType存储每种类型的附加数据,添加虚拟方法等.更重要的是,您可以通过创建新的实例来定义新的动物类型AnimalType.


Jef*_*nal 7

我敦促你重新考虑一下:在一个贫血的领域模型中(根据上面的评论),猫的行为与狗不同,所以没有多态性.动物的类型真的只是一个属性.很难看出继承在那里买什么.

  • 谢谢......"猫的行为与狗不同,因此没有多态性." 似乎是一个非常好的理由. (2认同)

jga*_*fin 6

有一个枚举就像为所有这些Open/Closed Principle is for suckers人举办派对.

它真的邀请您检查动物是否属于某种类型,然后为每种类型应用自定义逻辑.这可能会导致可怕的代码,这使得在您的系统上继续构建变得非常困难.