将公共操作和域放在超类
这就是为什么将姓名域放到Person类中,而不是放到Employee和Student类中的原因。
不要使用受保护的域
protected机制并不能够带来更好的保护,其原因是:1.子类集合是无限制的,任何一个人都能够由某个类派生一个类,并编写代码以直接访问protected的实例域,从而破坏了封装性。2.在Java中,在同一个包中的所有类都可以访问protected域,而不管它是否为这个类的子类。
使用继承实现“is-a”关系
使用继承很容易达到节省代码的目的。但是,有时候也被滥用。比如说,我们需要定义一个钟点工类,它包含姓名和雇佣日期,但是没有薪水。他们按小时计薪,并且不会因为拖延时间而加薪。这就诱导我们由Employee派生出子类Contractor,然后增加一个hourlyWage类。
这并不是一个好主意。因为,这样一来,每个钟点工对象中都包含薪水和计时工资这两个域。在实现打印支票和税单方法的时候,会产生无尽的麻烦。
所以,钟点工与雇员之间不属于“is-a”关系。钟点工不是特殊的雇员。我们在使用继承的时候,一定要注意父类与子类的联系。
除非所有继承的方法都有意义,否则不要使用继承
假设编写一个Holiday类。毫无疑问,假日也是一日,并且一日可以使用GregorianCalendar类的实例表示,因此可以使用继承。
class Holiday extends GregorianCalendar{.......}
但是,在继承的操作中,假日集不是封闭的,在GregorianCalendar中有一个公有方法add,可以将假日转换为非假日:
Holiday christmas;
christmas.add(Calendar.DAY_OF_MONTH,12);
因此,继承对于这个例子来说并不合适。
覆盖方法时,不要改变预期的行为
置换原则不仅应用于语法,而且也可以应用于行为,这更加重要。在覆盖一个方法的时候,不应该毫无原由地改变行为的内涵。就这一点而言,编译器不会提供任何帮助,即编译器不会检查重新定义的方法是否有意义。例如,可以重定义Holiday 类中的add方法“修改”原方法的问题,或什么也不做,或抛出一个异常,或继续到下一个假日。
然而这些都违反了置换原则。语句序列:
int d1=x.get(Calendar.DAY_OF_MONTH);
x.add(Calendar.DAY_OF_MONTH,1);
int d2=x.get(Calendar.DAY_OF_MONTH);
sout(d2-d1);
不管x属于GregorianCalendar类,还是属于Holiday类,执行上述语句后都应该得到预期的行为。所以,在覆盖子类方法的时候,不要偏离最初的设计想法。
使用多态而非类型信息
对于下面的这种代码:
if(x is type1){
action1(x);
}else if(x is type2){
action2(x);
}
就应该考虑使用多态。
action1和action2如果表示的是相同的概念,就应该为这个概念定义一个方法,并将其放置在两个类的超类或接口中,然后,就可以调用
x.action();
以便使用动态性提供的动态分派机制执行相应的动作。
使用多态方法或接口编写的代码比使用对多种类型进行检测的代码更加易于维护和扩展。
不要过多地使用反射
反射机制使得人们可以通过在运行时查看域和方法,让人们在编写出更具有通用性的程序。这种功能对于编写系统程序来说极其实用,但是通常不适用于编写应用程序。反射是很脆弱的,即编译器很难帮助人们发现程序中的错误,因此只有在运行时才发现错误并导致异常。