[译]OOSE第5章：面向对象的程序设计 5.5 Polymorphism
The algorithm discussed in the previous section shows clearly that the receiving instance is responsible for searching for and finding the appropriate operation to be executed. Polymorphism means that the transmitter of a stimulus does not need to know the class of the receiving instance. The transmitter provides only a request for a specified event, while the receiver knows how to perform this event. In this section, we discuss both how polymorphism can be implemented and how it behaves in relation to dynamic binding. The consequences for both typed and non-typed languages are also briefly discussed.
在上面章节中所讨论的算法已经非常清楚的介绍了激励的解释机制,具体来说是由接收到激励的实例对象负责和查找合适的操作来执行.多态Polymorphism就意味着一个激励的发送者不需要了解接收实例所归属的类.发送者仅仅需要提供一个申请特殊事件a specified event的请求,而事件的接收者知道如何执行这个事件. 在这个章节中,我们要分别讨论多态是如何实现的,以及多态和动态绑定dynamic binding概念的联系.而多态针对类型typed和非类型non-typed编程语言的影响也会简要的进行讨论.
The linking of the received stimulus to the appropriate operation to be executed is performed by binding the stimulus to this operation. If this binding occurs during compilation, it is said to be a static binding. The polymorphic characteristic sometimes makes it impossible to determine at compile time which class an instance belongs to and thus to decide which operation to perform. This information will not be available until execution time, when it must of course be known. If this binding occurs when the stimulus is actually sent, that is during run-time, it is said to be dynamic binding. Other names for this are late, delayed and virtual binding. Dynamic binding is flexible, but reduces performance; in principle, the operation lookup algorithm is now carried out during execution. This means that each time a stimulus is sent, the algorithm mentioned in the previous section must be executed. However, in most language implementations, sophisticated caching strategies or special function tables are used to minimize this overhead. The advantage with dynamic binding is the flexible system obtained. This flexibility is of most use to systems which are regularly modified. By not binding before execution, many of the modifications made will not affect the transmitting object.
将接收的激励和将要执行的对应操作进行链接的方式是通过把激励和操作进行绑定来实现(binding the stimulus to this operation.)假如这种绑定关系在编译的时候就已经确定,那么这就称为是静态绑定.而多态的特性有时会导致在编译的时间点还无法确定一个负责接收激励实例和类的归属关系,这样一来就无法决定具体哪个操作需要被执行. 这种信息可能需要等待操作需要执行的时刻才能够明确,因为此时必然需要被知道. 假如这种绑定过程发生在激励正在发送的时刻, 也就是说发生在运行的时刻,那么这就称为是动态绑定. 关于动态绑定的命名还有延迟绑定,晚绑定和虚拟绑定. 动态绑定这种方案提供了灵活性,但是降低了执行的效率,因为从原理上来说激励到操作的查找算法是在执行期间进行执行. 这就意味着每一次当一个激励被发送, 前面提到的算法就必须要执行一遍. 然而,在大多数的程序语言的实现过程中, 可以通过使用完备的缓存技术或者一个特殊的功能表的方法来最小化这种操作查找算法的时间.总而言之,使用动态绑定最大的一个优势就是获得了一个灵活架构的系统. 而灵活性flexibility最大的价值是体现在那些需要经常修改的系统中. 通过这种在激励发送执行后进行绑定的方式, 很多的调整操作将不会影响发送激励的对象.
Static binding, however, is more secure and efficient. It is more secure, if we have a typed language, owing to errors being noticed at the time of compilation and not left to cause failures during run-time. It is more efficient due to the operation look-up algorithm being performed only once during the compilation.
静态绑定的优势在于是更加的安全和充分. 首先这种静态绑定的执行方式会更加安全,假如我们拥有一个类型语言a typed language,这中安全性是归源于绑定错误会在编译的时间点被发现和解决,而不是延迟到运行阶段而引起失败错误.而充分性则是来自于操作查找算法(the operation look-up algorithm)仅仅在编译的时刻执行一次.
Polymorphism often conveys the message that the receiver of a stimulus cannot bind the stimulus to an operation before the stimulus is actually sent during run-time (as the class is unknown until then). If we do not know beforehand (during compilation) which operation is to be executed on the receipt of a certain stimulus, then we must use some kind of dynamic binding. Dynamic binding is therefore a way of implementing the polymorphism characteristic.
多态通常需要传送一个消息来标识当这些激励确切的发送之前(在运行环境下), 激励的接收者无法将激励和和一个特定的操作进行绑定, (因为当时接收的类还是未知的). 假如我们无法事先(在编译的时刻)知道接收到一个特定激励所对应的操作,那么我们就必须运用某种形式的动态绑定. 所以动态绑定是实现多态特性的一种可行的途径.
The word 'polymorphism' originates from Greek and means 'many forms' or 'many types'. This means that the referenced item can be of different types. How does this relate to non-typed languages such as Smalltalk? Is polymorphism useful for non-typed languages?Yes, it is. The confusion occurs because Smalltalk is often referred to as being a non-typed language. In reality, though, it is the variables in Smalltalk that lack type, whereas each instance has a very clear type, namely its class. It is this type that is referred to when discussing polymorphism in Smalltalk. In Smalltalk, polymorphism is normally not restricted through the use of an inheritance hierarchy (see below), as is often the case with strongly typed languages. In Smalltalk, the referenced instance can be associated with any class in the system.
单词多态('polymorphism')起源自希腊语言,他的含义是”很多形式”或者是”很多类型”. 这就意味这种参考的条目(the referenced item)有可能是很多不同的类型. 那么这种机制是如何关联到非类型的程序设计语言,比如Smalltalk. 多态这种机制在非类型的语言中还会继续应用吗. 答案是确定的. 这种疑惑是来自于Smalltalk通常被认定为一种非类型的语言. 在实际中,虽然在Smalltalk当中的变量variables缺少类型标识,然而每一个实例具有一个非常明确的类型标识,被称为他的类. 在Smalltalk中关于多态的讨论通常指的就是这种和具体实例相关联的类型. 在Smalltalk中,多态的应用通常不会受到层次化继承结构的限制, 因为在强类型的编程语言中,这种场景很容易发生. 在Smalltalk中,参考的实例可以和系统的任何类进行关联.
In strongly typed languages, however, such as Eiffel, Simula and C++, each reference to an instance has a type that specifies the classes to which the reference can refer. When the program text is written, the variables are declared and the stimuli to be sent are specified. Both the instance's class and the stimuli to be sent must be known at that time. Consequently, it should be known at the time of compilation which operations are to be executed and we should be able to bind the operation and stimulus statically. Is polymorphism then useful for strongly typed languages? Yes, but the reason is that we need not specify exactly with which class the receiving instance is associated. Typically we only specify that the instance shall be associated with class A or some of class A's descendants. The operation can be defined in a descendant and we thus cannot bind before execution, as it is only then known exactly with which class the instance is associated.
在一些强制指定类型的编程语言中,比如Eiffel, Simula and C++, 每个用于关联到一个实例的索引(reference)都有一个类型(type), 这个类型用于明确定义每个索引可以关联到那些类. 当程序设计者在编写程序代码时候, 这个变量需要提前声明,同时需要发送的激励要被定义. 实例对象关联的类和将要发送的激励都必须在编写程序的时刻加以明确. 在后续的过程中,在编译的时刻需要明确那些操作将要被执行,这样一来我们就可以将激励和操作进行静态的绑定.那么多态这种特性在强类型的面向对象的编程语言中还会有用吗? 答案当然是肯定的, 而此时多态能够有效的原因是在于我们不需要定义接收的实例对象和那个确切的类进行关联系.典型的实现方法是我们仅仅需要声明这个实例需要和一个类A, 或者是类A的一些后裔类有关系. 而操作则有可能在一个后裔类中进行定义, 这样一来我们在程序执行以前无法实现绑定, 因为自有在程序的执行时刻我们才能够确定一个实例和那一个类具体关联.
Polymorphism can, in fact, be used without having dynamic binding. This is the case if we have declared a variable containing a pointer to an instance, and the type of the variable is a parent of the instance's class, and the operation to be performed is declared in this parent class and is not overridden in any descendant. Irrespective of the descendant class with which the instance is associated, the exact operation will be known during compile time. We can then statically bind the operation to the stimulus. Note that we have still used polymorphism, as we do not know exactly to which class the actual instance belonged.
在事实上,多态这种技术可以在不使用动态绑定的情况下独立应用. 设想在有一种情况下,假如我们声明一个变量包含一个指针到一个实例对象, 其中变量的类型是这个实例的父亲类, 同时这个操作是在父亲类中进行声明, 而且不会被任何后裔类所覆盖. 无论这个实例是和那一个后裔类所关联, 在编译的时刻可以知道确切的操作。 我们可以把操作和激励进行绑定。但是注意，此时我们还是应用了多态，因为我们无法知道确切实例是属于那一个类的。
An uncertainty arises though, if the operation, in the above mentioned case, is redefined in a descendant. Can we still statically bind the operation? No, since we do not know the actual class associated with the instance and thus we do not know the correct operation. In Eiffel, dynamic binding will occur in all situations. In C++ and Simula, however, the language designer has in this case chosen to statically bind the stimulus to the operation known during compilation, namely that which applies in the parent class. This is then regarded as a deliberate choice by the programmer. If you want to delay operation binding until execution, you can declare an operation as 'virtual', which means that it can be redeclared in a descendant. Then we force the compiler to postpone the binding until run-time. In Eiffel, you can only declare an operation in a parent and force descendants to implement it (this is called a deferred routine in Eiffel). This is used when there is no implementation in the parent class to force the descendants to implement the operation. Figure 5.10 illustrates an example of the use of virtual operations.
在上面提到的案例中，假如相关的操作被重新定义到一个后裔类中，就会产生一种不确定性。我们还可以静态的绑定这个操作吗？显然是不行的，因为我们无法确定和实例确切关联的类是那一个，这样一来我们就无法知道确切的操作。 在Eiffel中，动态绑定会在各种场景下发生，然而在C++ 和Simula中，程序的设计者在这种场合下选择静态的绑定激励和那一个在编译时刻明确的操作，通常意味在在父亲类中实施。这种方式通常被认为是徒劳的。假如你希望把操作延迟绑定直到执行时刻，你可以声明一个操作为虚拟操作'virtual'，这就意味着这个操作可以在一个后裔类中进行重新声明。这样一来我们就可以强迫编译器延迟绑定操作直到运行时刻。在Eiffel中，你仅仅能够在父亲类中声明一个操作并且强迫后裔类来实现他（在Eiffel中，这种机制被称为是延迟动作a deferred routine。这种机制通常会应用在父亲类中没有实现，而强迫后裔类来实现相关的操作。图5-10 解释了一个关于虚拟操作的案例。
Dynamic binding can also be used without using polymorphism, as in Prolog for example, where a variable can be bound to an arbitrary term that has no type. Dynamic binding and polymorphism can thus be used independently of each other, and it is unfortunate that the two concepts are often confused in the object-oriented community.
动态绑定技术也可以独立与多态而使用。比如以Prolog为案例，一个变量可以绑定到一个无类型的主观条目（an arbitrary term）。这样一来动态绑定和多态可以彼此独立的应用，然而非常遗憾的是在面向对象的社区中，这两个概念通常会被混淆。