40

哪个类设计更好,为什么?

public class User
{
    public String UserName;
    public String Password;
    public String FirstName;
    public String LastName;
}

public class Employee : User
{
    public String EmployeeId;
    public String EmployeeCode;
    public String DepartmentId;
}

public class Member : User
{
    public String MemberId;
    public String JoinDate;
    public String ExpiryDate;
}

或者

public class User
{
    public String UserId;
    public String UserName;
    public String Password;
    public String FirstName;
    public String LastName;
}

public class Employee
{
    public User UserInfo;
    public String EmployeeId;
    public String EmployeeCode;
    public String DepartmentId;
}

public class Member
{
    public User UserInfo;
    public String MemberId;
    public String JoinDate;
    public String ExpiryDate;
}
4

11 回答 11

60

通过认识到继承模型是“IS-A”关系,而成员关系模型是“HAS-A”关系,可以简单地回答这个问题。

  • 员工就是用户
  • 员工有用户信息

哪一个是正确的?这是你的答案。

于 2008-09-02T04:41:11.573 回答
20

我不喜欢任何一个。当某人既是会员又是雇员时会发生什么?

于 2008-09-02T04:39:28.673 回答
17

问自己以下问题:

  • 你想建模一个员工一个用户吗?如果是,选择继承。
  • 你想建模一个员工一个用户信息吗?如果是这样,请使用组合。
  • 用户(信息)和员工之间是否涉及虚拟功能?如果是这样,请使用继承。
  • 员工可以有多个用户(信息)实例吗?如果是这样,请使用组合。
  • 将 Employee 对象分配给 User (info) 对象是否有意义?如果是这样,请使用继承。

通常,在代码复杂性和所需效率的约束下,努力对程序模拟的现实进行建模。

于 2008-09-02T05:03:48.960 回答
13

很好的问题,虽然为了避免对是非分心我会考虑询问每种方法的优缺点——我认为这就是你的意思,哪个更好或更坏以及为什么。反正 ....

第一种方法又名继承

优点:

  • 允许多态行为。
  • 最初是简单方便。

缺点:

  • 如果添加更多的行为和关系,可能会随着时间的推移变得复杂或笨拙。

第二种方法又名作文

优点:

  • 很好地映射到非 oop 场景,如关系表、结构化编程等
  • 直接(如果不一定方便)增量扩展关系和行为。

缺点:

  • 没有多态性,因此使用相关信息和行为不太方便

像这样的列表 + Jon Limjap提到的问题将帮助您做出决定并开始 - 然后您可以找到正确的答案应该是什么;-)

于 2008-09-09T04:07:23.107 回答
12

我不认为组合总是比继承更好(通常)。如果 Employee 和 Member 真的是用户,并且它们是互斥的,那么第一个设计更好。考虑您需要访问员工的用户名的场景。使用第二种设计,您将拥有:

myEmployee.UserInfo.UserName

这是不好的(得墨忒耳定律),所以你会重构:

myEmployee.UserName

这需要 Employee 上的一个小方法来委托给 User 对象。第一个设计避免了所有这些。

于 2008-09-02T05:46:09.577 回答
7

您还可以将 Employee 视为用户(Person)的角色。用户的角色可以随时更改(用户可能失业),或者用户可以同时拥有多个角色。

当存在真正的“是”关系时,继承会更好,例如 Apple - Fruit。但要非常小心:圆 - 椭圆不是真正的“是”关系,因为圆的“自由”比椭圆少(圆是椭圆的状态) - 请参阅:圆椭圆问题

于 2014-08-21T16:04:15.017 回答
5

真正的问题是:

  • 用户背后的业务规则和用户故事是什么?
  • 员工背后的业务规则和用户故事是什么?
  • 会员背后的业务规则和用户故事是什么?

这些可以是三个完全不相关的实体,这将决定您的第一个或第二个设计是否有效,或者是否需要另一个完全不同的设计。

于 2008-09-02T05:43:14.420 回答
4

没有一个是好的。太多的可变状态。您应该不能构造处于无效或部分初始化状态的类的实例。

也就是说,第二个更好,因为它有利于组合而不是继承。

于 2008-09-02T04:47:39.840 回答
3

说明您的要求/规格可能有助于达成“最佳设计”。
目前,您的问题过于“受读者解释”。

于 2008-09-02T07:43:01.197 回答
2

这是您应该考虑的一个场景:

如果同一个用户既可以是员工又可以是会员,则组合(第二个示例)更可取。为什么?因为对于代表同一个 User 的两个实例(Employee 和 Member),如果 User 数据发生变化,您不必在两个地方更新它。只有 User 实例包含所有 User 信息,并且只有它需要更新。由于 Employee 和 Member 类都包含相同的 User 实例,它们将自动包含更新的信息。

于 2008-09-02T07:54:57.707 回答
0

另外三个选项:

  1. User类包含员工和成员的补充信息,未使用的字段为空白(ID特定的User表示用户是员工、成员、两者还是其他)。

  2. 有一个User包含对 an 的引用的类ISupplementalInfo,其中ISupplementalInfoISupplementalEmployeeInfo,ISupplementalMemberInfo等继承。适用于所有用户的代码可以使用User类对象,并且具有User引用的代码可以访问用户的补充信息,但这种方法会User如果将来需要补充信息的不同组合,则不必更改。

  3. 如上所述,但让User类包含某种ISupplementalInfo. 这种方法的优点是便于在运行时向用户添加属性(例如,因为Member被雇用)。使用前一种方法时,必须为不同的属性组合定义不同的类;将“会员”转变为“会员+客户”需要与将“员工”转变为“员工+客户”不同的代码。后一种方法的缺点是,它会使防止冗余或不一致的属性变得更加困难(使用类似 a 的东西Dictionary<Type, ISupplementalInfo>来保存补充信息可能会起作用,但看起来有点“笨重”)。

我倾向于支持第二种方法,因为它比直接继承更适合未来的扩展。使用对象集合而不是单个对象可能会有点繁重,但这种方法可能比其他方法更能处理不断变化的需求。

于 2012-10-29T15:36:42.547 回答