下面会讲到一些小技巧,使得设计出来的类更具有 OOP 的专业水准。

一定要保证数据私有

这是最重要的;绝对不要破坏封装性。有时候,需要编写一个访问器方法或更改器方法,但是最好还是保持实例域的私有性。很多惨痛的经验告诉我们,数据的表示形式很可能会改变,但它们的使用方式却不会经常发生变化。当数据保持私有时,它们的表示形式的变化不会对类的使用者产生影响,即使出现 bug 也易于检测。

一定要对数据初始化

Java 不对局部变量进行初始化,但是会对对象的实例域进行初始化。最好不要依赖于系统的默认值,而是应该显式地初始化所有的数据,具体的初始化方式可以是提供默认值,也可以是在所有构造器中设置默认值。

不要在类中使用过多的基本类型,而是使用其他类来替代

就是说,用其他的类代替多个相关的基本类型的使用。这样会使类更加易于理解且易于修改。
例如,用一个称为 Address 的新的类替换一个 Customer 类中以下的实例域:

  1. private String street;
  2. private String city;
  3. private String state;
  4. private int zip;

这样,可以很容易处理地址的变化,例如,需要增加对国际地址的处理。

不是所有的域都需要独立的域访问器和域更改器

或许,需要获得或设置雇员的薪金。而一旦构造了雇员对象,就应该禁止更改雇用日期(可以使用 final 常量来指定),并且在对象中,常常包含一些不希望别人获得或设置的实例域。

将职责过多的类进行分解

如果明显地可以将一个复杂的类分解成两个更为简单的类,就应该将其分解(但另一方面,也不要走极端。设计 10 个类,每个类只有一个方法,显然有些矫枉过正了)。
来看一个反面案例:

  1. public class CardDeck
  2. {
  3. private int[] value;
  4. private int[] suit;
  5. public CardDeck() {...}
  6. public void shuffle() {...}
  7. public int getTopValue() {...}
  8. public int getTopSuit() {...}
  9. public void draw() {...}
  10. }

实际上,这个类实现了两个独立的概念:一副牌(含有 shuffle 方法和 draw 方法)和一张牌(含有查看面值和花色的方法)。可以这样改进,引入一个表示单张牌的 Card 类。现在有两个类,每个类完成自己的职责:

  1. public class CardDeck
  2. {
  3. private Card[] cards;
  4. public CardDeck() {...}
  5. public void shuffle() {...}
  6. public Card getTop() {...}
  7. public void draw() {...}
  8. }
  9. public class Card
  10. {
  11. private int value;
  12. private int suit;
  13. public Card(int aValue, int aSuit) {...}
  14. public int getTopValue() {...}
  15. public int getTopSuit() {...}
  16. }

类名和方法名要能够体现它们的职责

与变量应该有一个能够反映其含义的名字一样,类也应该如此。
命名类名的良好习惯是采用一个名词(Order)、前面有形容词修饰的名词(RushOrder)或动名词(有“-ing”后缀)修饰名词(例如,BillingAddress)。对于方法来说,习惯是访问器方法用小写 get 开头(getSalary),更改器方法用小写的 set 开头(setSalary)。

优先使用不可变的类

可变类会在多线程时发生问题。比如,多个线程试图同时更新一个对象,就会发生并发更改。其结果是不可预料的。如果类是不可变的,就可以安全地在多个线程间共享其对象。因为不可变类总是会生成一个新的对象。
当然,并不是所有类都应当是不可变的。如果员工加薪时让 raiseSalary 方法返回一个新的 Employee 对象,这会很奇怪。