如果一个类经过了充分测试,其代码就应该是有效且可复用的(理想情况)。不过,要实现这种复用性并不像想象的那么简单。创建可复用的对象设计需要大量的经验和洞见。然而,一旦你拥有了可复用的设计,不复用就可惜了。代码复用是我们使用面向对象编程的理由之一。
复用一个类最简单的方法是直接使用这个类所生成的对象,不过你也可以把这个对象放到另一个新类里面。新创建的类可以由任意数量和类型的对象组成,也可以任意组合这些对象,以满足想要的功能。因为利用已有的类组合成一个新的类,所以这个概念叫作“组合”(composition)。如果组合是动态的,通常叫作“聚合”(aggregation)。组合通常代表一种“有”(has-a)的关系,比如“汽车有发动机”(见图1-2)。
图1-2
图1-2中用箭头表示了一辆汽车的组合关系。而我习惯用一种更简单的方式,即一条没有箭头的直线来表达两者之间的关联。4
4这些信息对于大多数图来说已经足够了,也无须特别说明使用的是聚合还是组合。
组合为我们提供了极大的灵活性。这些在你的类内部创建的对象通常具有private属性,所以其他使用这个类的客户程序员无法访问它们。这也意味着,就算我们修改了这些内部对象,也不会影响外部已有的代码。此外,你还可以在运行时改变这些内部对象,从而动态调整程序的行为。下一节要讲述的继承机制则不具备这种灵活性,因为编译器对使用继承创建的类设置了一些编译时的限制。
继承常被视为面向对象编程的重中之重,因此容易给新手程序员留下这样的印象:处处都应该使用继承。而实际上,这种全盘继承的做法会导致设计变得十分别扭和过于复杂。所以相比之下,在创建新类时应该首先考虑组合,因为使用组合更为简单灵活,设计也更为清晰简洁。一旦你拥有了足够的经验,何时使用继承就会变得非常清晰了。