具体类型的问题
一个具体类型就像通过模块定义的一个“假类型”一样,定义了一类黑盒子。一旦这种黑盒子定义好之后,它也就无法再与程序的其他部分进行实际地交互了:没有任何方式可以为了某些新的用途而调整它,除非去修改它的定义。这种情况可以认为是非常理想的,但也会导致严重缺乏灵活性。考虑在某个图形系统里定义一个类型 Shape。假定当时这个系统需要支持圆形、三角形和正方形,再假定我们已经有了
class Point{ /* ... */ };
class Color{ /* ... */ };
这里的 /*
和 */
分别表示一个注释的开始和结束,这种注释形式可以用于多行的注释,以及在一行结束之前结束的那种注释。
我们可能像下面这样定义形状(shape)类:
enum Kind { circle, triangle, square };
class Shape {
Kind k; // 类型域
Point center;
Color col;
// ...
public:
void draw();
void rotate(int);
// ...
};
这里的“类型域” k
是必需的,以便使 draw()
和 rotate()
一类的函数能够确定它们正在处理的是哪种形状(在类 Pascal 的语言里,人们可以使用带有标志 k 的变体纪录)。函数 draw()
的定义可能具有下面的样子:
void Shape::draw()
{
switch(k){
case circle:
// 画圆
break;
case triangle:
// 画三角形
break;
case square:
// 画正方形
break;
}
}
这真是一个烂泥潭,像 draw()
这一类函数“必须知道”现存的所有形状的种类。因此,每当有一种新形状被加进系统时,这类函数中每一个的代码都需要增加。如果我们定义了一种新形状,就需要检查每个有关形状的操作并(可能)做些修改。除非我们能使用所有操作的源代码,否则我们就无法向这个系统中加进一个新形状。由于增加一个新形状涉及到“触动”在形状上的每个重要操作的代码,这样做的时候就需要极高的技术水平,也很可能将错误引进处理其他(老)形状的代码中。还有,对于特定形状的表示方式的选择也受到严重的束缚,因为它们的表示(至少是某些部分)必须塞进由通用类型 Shape 的定义所表达的常常是具有固定大小的框架之中。
🔚