最近我发现一些代码将子类型声明为基类中的枚举。这是一个简单的例子:
public enum EmployeeType
{
Manager,
Secretary
}
public class Employee
{
public string Code { get; set; }
public EmployeeType Type { get; set; }
}
public class Manager : Employee
{
public void Manage()
{
// Managing
}
}
public class Secretary : Employee
{
public void SetMeeting()
{
// Setting meeting
}
}
根据我的开发经验,我写了an article关于它,宣布这是一种不好的做法/设计。我认为这很糟糕,因为基类应该对其子类不可知。它应该没有关于其子类的信息,这里至少有两个原因:
- 可扩展性:此设计将不可扩展,因为如果您想定义另一个派生类,例如
Developer
,您还应该更新EmployeeType
枚举,您可能无权访问的内容。 矛盾的定义:现在你可以写出这段代码:
Secretary secretary = new Secretary(); secretary.EmployeeType = EmployeeType.Manager; /* This is absurd semantically. But syntactically, it's possible. */
然而,当我读到Wikipedia's article about inheritance , 我找不到我的问题的任何答案。
虽然乍一看可能有争议,但我相信继承应该足够成熟,可以为这个难题提供可靠的答案。这段代码不好吗,smelly code ?还是可以接受和合理的?为什么?
最佳答案
我认为当我们回到何时使用继承的哲学时,问题就可以解决。
据说“将继承视为是一种关系”
当您在两个类之间建立继承关系时,您可以利用动态绑定(bind)和多态性。
动态绑定(bind) 是指在运行时调用哪个方法实现,基于对象的类。
多态性 意味着您可以使用父类(super class)类型的变量来保存对其类是父类(super class)或其任何子类的对象的引用。
动态绑定(bind)和多态性的主要好处之一是它们可以帮助使代码更容易更改。如果您有一段代码使用父类(super class)类型的变量,例如 Employee,您可以稍后创建一个全新的子类,例如 Manager,并且旧代码片段将在新子类的实例中不做任何更改。如果 Manager 覆盖代码片段调用的任何 Employee 方法,动态绑定(bind)将确保 Managers 对这些方法的实现得到执行。即使在编写和编译代码片段时类管理器不存在,也是如此。
现在回到问题,为什么我们需要 Employee 中的 EmployeeType?
可能有一些我们想在 Employee 中实现的功能,也就是 CalculateSalary()
CalculateSalary(){
if (EmployeeType == EmployeeType.Manager) // Add management percent }
理由很诱人,但如果员工既是经理又是主管怎么办?
它会很复杂!我们可以简单地将 CalculateSalary() 实现为抽象方法,但我们会失去目标!
Coad 规则来了:
子类表示是一种特殊的并且不是a所扮演的角色。
Liskov Substitution原则是对“我应该从这种类型继承吗?”的测试
以上拇指的作用是:
- TypeB 是否要公开完整的接口(interface)(所有公共(public)方法
不少于)TypeA 使得 TypeB 可以在 TypeA 所在的地方使用
预期的?表示继承。
例如塞斯纳双翼飞机将暴露 飞机的完整界面,如果不是更多的话。所以这使得它 适合从飞机派生。 - TypeB 是否只想要 TypeA 公开的部分/部分行为?
表示需要组合。
例如一只鸟可能只需要苍蝇 飞机的行为。在这种情况下,提取它是有意义的 out 作为接口(interface)/类/两者并使其成为两者的成员 类(class)。
作为脚注来自 javaworld 强>:
确保继承对 is-a 关系建模。
不要仅仅为了代码重用而使用继承。
不要仅仅为了多态而使用继承。
优先考虑组合而不是继承。
我觉得在这种场景下,管理者是员工扮演的角色,这个角色可能会随着时间的推移而改变,这个角色会作为一个组合来实现。
作为域模型中 View 的另一个方面,并将其映射到关系数据库。将继承映射到 SQL 模型真的很难(您最终会创建并不总是使用的列,使用 View 等)。一些 ORM 试图处理这个问题,但它总是很快变得复杂。
例如 Hibernate 的 One Table Per Class Hierarchy 方法,违反了 2NF并且很可能导致您的样本不一致
Secretary secretary = new Secretary();
secretary.EmployeeType = EmployeeType.Manager;
关于c# - 在基类中声明子类;坏不坏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20018266/