corejava11(5.8 继承的设计提示)

5.8 继承的设计提示

我们想在本章结尾给出一些提示,说明在使用继承时我们发现这一点很有用。

  1. 在超类中放置通用操作和字段。
    这就是为什么我们将name字段放入Person类,而不是在Employee和Student类中复制它。

  2. 不要使用protected字段。
    一些程序员认为,最好将大多数实例字段定义为protected,“以防万一”,这样子类可以在需要时访问这些字段。然而,由于两个原因,protected机制并没有提供太多的保护。首先,子类集是无边界的,任何人都可以形成类的子类,然后编写直接访问受保护实例字段的代码,从而破坏封装。第二,在Java中,同一包中的所有类都可以访问protected字段,而不管它们是否是子类。
    但是,protected方法可以用来指示尚未准备好一般使用的方法,并且应该在子类中重新定义这些方法。

  3. 使用继承为“is-a”关系建模。
    继承是一个方便的代码保护程序,但有时人们会过度使用它。例如,假设我们需要一个Contractor类。承包商有名字和雇佣日期,但他们没有薪水。相反,他们的工资是按小时计算的,而且他们没有足够长的时间来获得加薪。有一种诱惑,即从Employee中形成一个子类Constractor,并添加一个hourlyWage字段。

    public class Contractor extends Employee
    {
        private double hourlyWage;
        . . .
    }
    
    

    然而,这不是一个好主意,因为现在每个承包商对象都有一个工资和小时工资字段。当你执行打印工资单或纳税申报表的方法时,它将使你的悲伤永无止境。如果不从一开始就继承代码,那么最终编写的代码会比继承代码编写的代码多。
    承包商员工关系未通过“is-a”测试。承包商不是雇员的特例。

  4. 除非所有继承的方法都有意义,否则不要使用继承。
    假设我们想写一个Holiday类。当然,每个假日都是一天,并且可以将天表示为GregorianCalendar类的实例,因此我们可以使用继承。

    class Holiday extends GregorianCalendar { . . . }
    
    

    不幸的是,在继承的操作下,假日集没有关闭。GregorianCalendar的一个公共方法是addadd可以将假日转换为非假日:

    Holiday christmas;
    christmas.add(Calendar.DAY_OF_MONTH, 12);
    
    

    因此,在这个例子中继承是不合适的。
    请注意,如果扩展LocalDate,则不会出现此问题。因为该类是不可变的,所以没有任何方法可以将假日转换为非假日。

  5. 重写方法时不要更改预期的行为。
    替代原则不仅适用于语法,更重要的是适用于行为。当您重写一个方法时,您不应该不合理地更改它的行为。编译器不能帮助你,它不能检查你的重新定义是否有意义。例如,您可以通过重新定义add来“修复”holiday类中add方法的问题,也许什么都不做,或者抛出异常,或者转到下一个holiday。
    然而,这样的修复违反了替换原则。语句的顺序

    int d1 = x.get(Calendar.DAY_OF_MONTH);
    x.add(Calendar.DAY_OF_MONTH, 1);
    int d2 = x.get(Calendar.DAY_OF_MONTH);
    System.out.println(d2 - d1);
    
    

    应该有预期的行为,不管x是GregorianCalendar还是Holiday
    当然,这也存在困难。理性和不理性的人可以对预期行为进行详细的争论。例如,一些作者认为替换原则要求Manager.equals忽略bonus字段,因为Employee.equals忽略它。如果这些讨论发生在真空中,那就没有意义了。最后,重要的是当您覆盖子类中的方法时,您不会规避原始设计的意图。

  6. 使用多态性,而不是类型信息。
    每当你找到表格的代码

    if (x is of type 1)
        action1(x);
    else if (x is of type 2)
        action2(x);
    
    

    想想多态性。
    action1和action2是否代表一个共同的概念?如果是这样的话,就让这个概念成为两种类型的公共超类或接口的方法。那么,你只需调用

    x.action();
    
    

    并具有多态性固有的动态调度机制,启动正确的动作。
    使用多态方法或接口实现的代码比使用多类型测试的代码更易于维护和扩展。

  7. 不要过度使用反射。
    反射机制允许您通过在运行时检测字段和方法,以惊人的通用性编写程序。这种能力对于系统编程非常有用,但在应用程序中通常不合适。反射很脆弱,编译器无法帮助您发现编程错误。在运行时发现任何错误并导致异常。

现在您已经看到Java如何支持面向对象编程的基础:类、继承和多态性。在下一章中,我们将讨论两个高级主题,这些主题对于有效使用Java是非常重要的:接口和lambda表达式。

猜你喜欢

转载自blog.csdn.net/nbda1121440/article/details/90762452
今日推荐