( A b s t r a c t F a c t o r y ( 3 . 1 ),B u i l d e r (
3 . 2 ),F a c t o r y M e t h o d ( 3 . 3 ),P
r o t o t y p e ( 3 . 4 )和S i n g l e t o n ( 3 . 5 ) )能够帮你。通过抽象对象的创建过程,这些模式提供不同方式以在实例化时建立接口和实现的透明连接。创建型模式确保你的系统是採用针对接口的方式书写的,而不是针对实现而书写的。
1.6.5运用复用机制
理解对象、接口、类和继承之类的概念对大多数人来说并不难,问题的关键在于如何运用它们写出灵活的、可复用的软件。
设计模式将告诉你如何去做。
1.继承和组合的比較
面向对象系统中功能复用的两种最经常使用技术是类继承和对象组合(object composition)。
正如我们已解释过的,类继承同意你依据其它类的实现来定义一个类的实现。这样的通过生成子类的复用通常被称为白箱复用(white-box
reuse)。术语“白箱”是相对可视性而言:在继承方式中,父类的内部细节对子类可见。
对象组合是类继承之外的还有一种复用选择。新的更复杂的功能能够通过组装或组合对象来获得。
对象组合要求被 组合的对象具有良好定义的接口。这样的复用风格被称为黑箱复用(black-box
reuse),由于对象的内部细节是不可见的。对象仅仅以“黑箱”的形式出现。
继承和组合各有优缺点。类继承是在编译时刻静态定义的,且可直接使用,由于程序设计语言直接支持类继承。
类继承能够较方便地改变被复用的实现。当一个子类重定义一些而不是所有操作时,它也能影响它所继承的操作,仅仅要在这些操作中调用了被重定义的操作。
可是类继承也有一些不足之处。
首先,由于继承在编译时刻就定义了,所以无法在执行时刻改变从父类继承的实现。更糟的是,父类通常至少定义了部分子类的详细表示。
由于继承对子类揭示了其父类的实现细节,所以继承常被觉得“破坏了封装性” [ S
n y 8 6 ]。
子类中的实现与它的父类有如此紧密的依赖关系,以至于父类实现中的不论什么变化必定会导致子类发生变化。
当你须要复用子类时,实现上的依赖性就会产生一些问题。
假设继承下来的实现不适合解决新的问题,则父类必须重写或被其它更适合的类替换。这样的依赖关系限制了灵活性并终于限制了复用性。
一个可用的解决方法就是仅仅继承抽象类,由于抽象类通常提供较少的实现。
对象组合是通过获得对其它对象的引用而在执行时刻动态定义的。组合要求对象遵守彼此的接口约定,进而要求更细致地定义接口,而这些接口并最好还是碍你将一个对象和其它对象一起使用。这还会产生良好的结果:由于对象仅仅能通过接口訪问,所以我们并不破坏封装性;仅仅要类型一致,执行时刻还能够用一个对象来替代还有一个对象;更进一步,由于对象的实现是基于接口写的,所以实现上存在较少的依赖关系。
对象组合对系统设计还有还有一个作用,即优先使用对象组合有助于你保持每一个类被封装,并被集中在单个任务上。
这样类和类继承层次会保持较小规模,而且不太可能增长为不可控制的庞然大物。
还有一方面,基于对象组合的设计会有很多其它的对象 (而有较少的类),且系统的行为将依赖于对象间的关系而不是被定义在某个类中。
这导出了我们的面向对象设计的第二个原则:
优先使用对象组合,而不是类继承。
理想情况下,你不应为获得复用而去创建新的构件。你应该可以仅仅使用对象组合技术,
通过组装已有的构件就能获得你须要的功能。
可是事实非常少如此,由于可用构件的集合实际上并不足够丰富。使用继承的复用使得创建新的构件要比组装旧的构件来得easy。这样,继承和对象组合常一起使用。