OOP真正的革命性在于它使用间接来调用代码。
不是直接调用某个函数,而是间接调用。
只要能够理解这一点,就算是掌握了OOP的内涵了。
其他一切都是通过间接产生的引申结果。
为了理解OOP的灵活性,我们可以先了解什么是过程式编程,这样就可以互相比较。
如果学习过C语言,那么应该可以明白过程式编程是怎么回事。
在过程式编程中,数据通常保存在简单的结构体中(例如C语言的struct元素)。还有一些复杂的数据结构,比如链表和树。当调用一个函数时,你必须将数据传递给函数,函数会处理这些数据。在过程式编程中会频繁地使用到函数,由使用者决定使用什么函数,然后调用它并传递其所需的数据。
以下是一个绘制几何形状的程序,其中省略了绘图代码,并用输出字符代替:
1、必须包含Foundation框架;
2、通过枚举指定几种可以绘制的形状(圆形、矩形和椭圆形):
#import <Foundation/Foundation.h>
typedef enum{
kCircle,
kRectangle,
kEgg } ShapeType; //枚举形状
3、接着枚举绘制形状时的可用颜色:
typedef enum{
kRedColor,
kGreenColor,
kBlueColor } ShapeColor; //枚举可用颜色
4、接着使用一个结构体来描述一个矩形,该矩形指定了屏幕上的绘图区域(在此仅以矩形举例):
typedef struct{
int x, y, width, height; //绘图坐标点及绘图区域边长
} ShapeRect;
5、最后,用一个结构体把前面的所有内容包含进来:
typedef struct{
ShapeType type;
ShapeColor fillColor;
ShapeRect bounds;
} Shape;
以上的几个程序块整体地描述了一个形状。
接下来,main()函数声明了要绘制的形状的数组。声明完数组后,数组中的每个元素都通过分配空间进行初始化。以下是示例程序:
int main(int argc, const char * argv[])
{
Shape shapes[3];
ShapeRect rect0 = { 0, 0, 10, 30 };
shapes[0].type = kCircle;
shapes[0].fillColor = kRedColor;
shapes[0].bounds = rect0; //红色圆形
ShapeRect rect1 = { 30, 40, 50, 60 };
shapes[1].type = kRectangle;
shapes[1].fillColor = kGreenColor;
shapes[1].bounds = rect1; //绿色矩形
ShapeRect rect2 = { 15, 18, 37, 29 };
shapes[2].type = kEgg;
shapes[2].fillColor = kBlueColor;
shapes[2].bounds = rect2; //蓝色椭圆形
drawShapes(shapes, 3); //执行绘制动作的函数
return 0;
}
其中,main()调用了drawShapes()函数来绘制形状。drawShapes()函数中的循环先检查数组中的每个元素(每个Shapes结构体),再通过switch语句查看结构体的type字段并调用合适的绘制函数:
void drawShapes(Shape shapes[], int count)
{
for (int i = 0; i < count; i++)
{
switch (shapes[i].type)
{
case kCircle:
drawCircle (shapes[i].bounds, shapes[i].fillColor);
break;
case kRectangle:
drawRectangle (shapes[i].bounds, shapes[i].fillColor);
break;
case kEgg:
drawEgg (shapes[i].bounds, shapes[i].fillColor);
break;
}
}
}
举例其中的drawCircle()函数,该函数输出矩形区域的信息及图形颜色:
void drawCircle(ShapeRect bounds, ShapeColor fillColor)
{
NSLog (@"drawing a circle at (%d %d %d %d) in %@",
bounds.x, bounds.y,
bounds.width, bounds.height,
colorName(fillColor));
}
在drawCircle()函数中,NSLog()中调用的colorName()函数负责转换传入的颜色值,并返回NSString字面量:
NSString *colorName(ShapeColor colorName)
{
switch (colorName)
{
case kRedColor:
return @"red";
break;
case kGreenColor:
return @"green";
break;
case kBlueColor:
return @"blue";
break;
}
return @"no clue"; //@意味着引号内部的字符串应该作为Cocoa的NSString元素来处理。
}
drawing a circle at (0 0 10 30) in red
drawing a rectangle at (30 40 50 60) in green
drawing an egg at (15 18 37 29) in blue
按理来说,程序已经正常运行,那就可以了。但是必须注意,如果该程序需要添加一个新的形状,比如三角形,那我们就要花时间把三角形的相关程序重新写到各个程序里。万一要添加形状和颜色很多,那正确地维护程序将变得困难。
我们将这种情况称为,程序的扩展性差。
这个时候,我们必须考虑OOP。