4.3.6 封装的优点
最后,再仔细地看一下非常简单的getName方法、getSalary方法和getHireDay方法。
这些都是典型的访问器方法。由于它们只返回实例域值,因此又称为域访问器。
将name、salary和hireDay域标记为public,以此来取代独立的访问器方法会不会更容易些呢?
关键在于name是一个只读域。一旦在构造器中设置完毕,就没有任何一个办法可以对它进行修改,这样来确保name域不会受到外界的破坏。
虽然salary不是只读域,但是它只能用raiseSalary方法修改。特别是一旦这个域值出现了错误,只要调试这个方法就可以了。如果salary域是public的,破坏这个域值的捣乱者有可能会出没在任何地方。
在有些时候,需要获得或设置实例域的值。因此,应该提供下面三项内容:
●一个私有的数据域;
●一个公有的域访问器方法;
●一个公有的域更改器方法。
这样做要比提供一个简单的公有数据域复杂些,但是却有着下列明显的好处:
首先,可以改变内部实现,除了该类的方法之外,不会影响其他代码。
例如,如果将存储名字的域改为:
那么getName方法可以改为返回
对于这点改变,程序的其他部分完全不可见。
当然,为了进行新旧数据表示之间的转换,访问器方法和更改器方法有可能需要做许多工作。但是,这将为我们带来了第二点好处:更改器方法可以执行错误检查,然而直接对域进行赋值将不会进行这些处理。例如,setSalary方法可以检查薪金是否小于0。
警告:注意不要编写返回引用可变对象的访问器方法。在Employee类中就违反了这个设计原则,其中的getHireDay方法返回了一个Date类对象:
这样会破坏封装性!请看下面这段代码:
出错的原因很微妙。d和harry.hireDay引用同一个对象(请参见图4-5)。对d调用更改器方法就可以自动地改变这个雇员对象的私有状态!
图4-5 返回可变数据域的引用
如果需要返回一个可变对象的引用,应该首先对它进行克隆(clone)。对象clone是指存放在另一个位置上的对象副本。有关对象clone的详细内容将在第6章中讨论。下面是修改后的代码:
凭经验可知,如果需要返回一个可变数据域的拷贝,就应该使用clone。