2.19 对接口的再认识
C++面向对象高效编程(第2版)
我们已经学习了接口在OOP中的重要性,也理解了接口和实现之间的关系。每个对象都支持一组操作(消息或成员函数)。在声明操作时,需要指定操作的名称、被当做参数的对象,以及操作的返回值。这就是之前提到的操作的签名(或操作/函数的原型)。由对象操作定义的所有签名的集合,称为该对象的接口。
类型(type)是用于表示特殊接口的名称,它使得接口易于管理。否则,接口之间就无法区分。如果一个对象支持定义在TInt接口中的所有操作,则该对象的类型就是TInt。一个对象可能有许多类型,换言之,一个对象可以支持(或响应)多个接口。如果两个对象都支持相同的接口,则它们的类型相同,但两者的实现可能完全不同。
因此,现在你可能认为,类只不过是一个接口而已。在某些语言中,的确如此。但是,它们还是略有不同。有必要理解对象的类(class)和类型(type)之间的区别。对象的类定义如何实现对象,它定义对象的内部表示以及操作的实现。然而,对象的类型与实现无关——它只涉及对象可响应的操作集合。一个对象可以有多种类型,不同的类可以有相同的类型。但是另一方面,类和类型的关系又十分密切。类清晰地定义了该类对象可执行的操作,因此,类也定义了对象的类型。类的任何对象都支持该类定义的接口。
在C++(和Eiffel)中,类同时指定了对象的类型和它的实现。C++和Eiffel都不能在类的外部定义接口。也就是说,接口只能通过类指定1。类似Java的语言,允许程序员无需依附任何类就能定义接口,类可以稍后实现一个接口(即类对象支持的接口)。在Smalltalk中,程序员无需指定变量的类型。在运行期,将消息发送给对象时,必须经过检查,以确保接收对象可实现该消息,但并不要求对象必须是某个特定类的实例。
经过以上的讨论,我们还要学习许多重要的课程。在实际应用中,最好是编写仅依赖接口的软件,而不是依赖特定实现的软件。如果软件不与任何特定实现绑定,一个接口可以由许多不同实现支持,那么,在使用支持接口的新对象时,也不必时常更改软件。这正是我们在软件设计中力争达到的目标,软件越独立于实现越好。
有时,就算我们知道一些实现细节,也应装作一无所知。即使实现做了改动(甚至是很大的改动),我们也不必为此担心,因为接口仍保持不变。但是,在许多情况下,程序员会在程序中(有意或无意地)涉及一些实现的细节。当处理对象时,这样的程序就非常依赖于特定的实现,迟早都会导致设计和实现的重大变化。
从另一个方面看(即接口设计方面),接口不应该暴露实现细节。精心设计的接口不应该要求客户了解实现的任何细节。如果在接口中暴露了某些实现细节(甚至是非常微小的),一些客户就会期望获得所有的实现细节,从而使得软件非常脆弱,毫无稳定性可言。