——- android培训、IOS培训、期待与您交流! ———-
面向对象编程
要理解OOP的灵活性,首先来看一下过程式编程。
下面一个程序假设在屏幕上绘制几何图形,不过用文本输出代替了实际的绘图,:
#import <Foundaion/Foundation.h>
//通过枚举指定可以绘制的图形
typedef enum {
Circle,//圆形
Rectangle,//矩形
Egg,//椭圆形
} ShapeType;
//定义绘制形状时可以使用的颜色
typedef enum {
RedColor,//红色
GreenColor,//绿色
BlueColor,//蓝色
} ShapeColor;
// 描述一个矩形,该矩形指定屏幕上的绘图区域
typedef struct {
int x;
int y;
int width;
int height;
} ShapeRect;
//综合所有的内容,成为一个整体
typedef struct {
ShapeType type;
ShapeColor fillColor;
ShapeRect bounds;
} Shape;
int main(int argc, char *argv[]) {
Shape shapes[3]; //声明要绘制的形状的数组
ShapeRect rect0= { 0, 0, 10, 30 }; //定义绘图空间
shapes[0].type = Circle; //圆形
shapes[0].fillColor = RedColor; //红色
shapes[0].bounds = rect0; //分配绘图空间
ShapeRect rect1= { 30, 40, 50, 60 }; //定义绘图空间
shapes[1].type = Rectangle; //矩形
shapes[1].fillColor = GreenColor; //绿色
shapes[1].bounds = rect1; //分配绘图空间
ShapeRect rect2= { 15, 18, 37, 29 }; //定义绘图空间
shapes[2].type = Egg; //椭圆形
shapes[2].fillColor = BlueColor; //蓝色
shapes[2].bounds = rect2; //分配绘图空间
drawShapes(shapes, 3); //调用drawShapes()函数绘制形状
return 0; //main函数返回
} //main
drawShapes()
中的循环先检查数组中的每个Shape 结构体,再通过switch 语句查看结构体的type 字段并调用适合该形状的绘制函数,传递绘图区域和颜色的参数。代码如下:
void drawShapes(Shape shapes[], int count) {
for(int i = 0; i < count, i++) {
switch(shapes[i].type) {
case Circle:
drawCircle(shapes[i].bounds,
shapes[i].fillColor );
break;
case Rectangle:
drawRectangle(shapes[i].bounds,
shapes[i].fillColor );
break;
case egg:
drawEgg(shape[i].bounds,
shape[i].fillColor);
break;
}
}
} //drawShapes
下面是 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
colorName()
函数负责转换传入的颜色值,并返回NSString
字面量,比如 @”red”或 @”blue”。
NSString *ColorName (ShapeColor colorName) {
swith (colorName) {
case RedColor:
return @"red";
break;
case GreenColor:
return @"green";
break;
case BlueColor:
return @"blue";
break;
}
return @"No clue";
} //ColorName
drawRectangle()
和drawEgg()
绘图程序与·drawCircle()·类似。
使用过程式编程时,要花时间连接数据和用来处理该数据的函数。必须注意要为数据使用正确的函数。
这种代码的另一个问题是程序的扩展和维护变得困难了。作为例子,为此程序添加一种新形状:三角形。完成任务必须至少修改程序中4个不同的位置。
首先,在ShapeType enum
枚举的内容中增加Triangle 常量。
typedef enum{
Circle;
Rectangle;
Egg;
Triangle;
}ShapeType;
然后编写drawTriangle()
函数
void drawTriangle(ShapeRect bounds,ShapeColor fillColor) {
NSLog (@"drawing triangle at (%d %d %d %d) in %@",
bounds.x,
bounds.y,
bounds.height,
colorName(fillColor)
) //drawTriangle
}
接下来,在drawShape()
的switch
语句中增加一个新的case
判断,用于测试当前性状的type
值是不是Triangle ,如果通过的话则调用drawTriangle()
函数。
void drawShapes(Shape shapes[], int count) {
for(int i = 0; i < count, i++) {
switch(shapes[i].type) {
case Circle:
drawCircle(shapes[i].bounds,
shapes[i].fillColor );
break;
case Rectangle:
drawRectangle(shapes[i].bounds,
shapes[i].fillColor );
break;
case egg:
drawEgg(shape[i].bounds,
shape[i].fillColor);
break;
case Triangle:
drawTriangle(shape[i].bounds,
shape[i].fillColor);
break;
}
}
} //drawShapes
最后,为Shape数组增加一个三角形。
int main(int argc, char *argv[]) {
Shape shapes[4]; //声明要绘制的形状的数组
ShapeRect rect0= { 0, 0, 10, 30 }; //定义绘图空间
shapes[0].type = Circle; //圆形
shapes[0].fillColor = RedColor; //红色
shapes[0].bounds = rect0; //分配绘图空间
ShapeRect rect1= { 30, 40, 50, 60 }; //定义绘图空间
shapes[1].type = Rectangle; //矩形
shapes[1].fillColor = GreenColor; //绿色
shapes[1].bounds = rect1; //分配绘图空间
ShapeRect rect2= { 15, 18, 37, 29 }; //定义绘图空间
shapes[2].type = Egg; //椭圆形
shapes[2].fillColor = BlueColor; //蓝色
shapes[2].bounds = rect2; //分配绘图空间
ShapeRect rect3= { 15, 18, 37, 29 }; //定义绘图空间
shapes[3].type = Triangle; //三角形
shapes[3].fillColor = RedColor; //红色
shapes[3].bounds = rect3; //分配绘图空间
drawShapes(shapes, 4); //调用drawShapes()函数绘制形状
return 0; //main函数返回
} //main
下面是该程序的面向对象版本:
void drawShapes (id shapes[], int count) {
for (int i = 0; i < count, i++) {
id shape = shapes[i];
[shape draw];
}
} //drawShapes
该程序包含一个循环,用于遍历数组中的每个形状。循环过程中,程序通知形状对象绘制自身。
程序中id
表示标识符(identifier),可以用来引用任意类型的对象,本质上是一个指向结构体的指针。
id shape = shapes[i];
是一个指针赋值过程,实际并没有复制shape的全部内容。
[shape draw];
在Objective-C中,方括号还有其他含义: 用于通知某个对象该去做什么。方括号里的第一项是对象,其余部分是需要对象执行的操作。
下面定义Circle类的接口:
@interface Circle: NSObject {
@private
ShapeColor fillColor;
ShapeRect bounds;
}
- (void) setFillColor: (ShapeColor) fillColor;
- (void) setBounds: (ShapeRect) bounds;
- (void) draw;
@end //Circle
@interface Circle: NSObject
这一行里的@interface circle
表示这是“新类circle
的接口”。: NSObject
表示新类circle
继
承自类NSObject
。
{
@private
ShapeColor fillColor;
ShapeRect bounds;
}
括号里面的是成员变量
- (void) setFillColor: (ShapeColor) fillColor;
- (void) setBounds: (ShapeRect) bounds;
- (void) draw;
这三行是方法说明,列出了每个方法的名称、方法返回值的类型和某些参数。
@interface
部分是用来定义类的公共接口,而使对象运行的代码位于@implementation
部分中。
以下是Circle类的实现:
@implementation Circle
- (void) setFillColor : (ShapeColor) c
{
fillcolor = c;
} //setFillColor
- (void) setBounds : (ShapeRect) b
{
bounds = b;
} //setBounds
- (void) draw {
NSLog (@"drawing Circle at (%d %d %d %d) in %@",
bounds.x,
bounds.y,
bounds.height,
colorName(fillColor)
)
} //draw
@end //Circle
使用main()
创建圆形、矩形和椭圆。
int main() {
id shape[3];
ShapeRect rect0 = {0, 0, 10, 30};
shapes[0] = [Circle new];
[shapes[0] setBounds: rect0];
[shapes[0] setFillColor: RedColor];
ShapeRect rect1 = {30, 40, 50, 60};
shapes[1] = [Rectangle new];
[shapes[1] setBounds: rect1];
[shapes[1] setFillColor: GreenColor];
ShapeRect rect2 = {15, 19, 37, 29};
shapes[2] = [Egg new];
[shapes[2] setBounds: rect2];
[shapes[2] setFillColor: BlueColor];
drawShapes(shapes,3);
return 0;
} //main
这里使用了id数组而不是shapes 数组。通过向需要创建对象的类发送new 方法,可以创建多个独立对象。
过程式编程通过直接设置struct 的成员来初始化对象,面向对象版本则不是直接设置对象的值,而是而是使用消息请求每个对象设置它自身的边界区域和填充颜色。
下面为该程序的面向对象版本添加绘制三角形的功能。相比于过程式编程的繁琐,这里我们只需要做两件事: 创建新的Triangle 类,然后将triangle对象添加到将要绘制的对象列表中去。
@interface Triangle: NSObject {
@private
ShapeColor fillColor;
ShapeRect bounds;
}
- (void) setFillColor: (ShapeColor) fillColor;
- (void) setBounds: (ShapeRect) bounds;
- (void) draw;
@end //Triangle
@implementation Triangle
- (void) setFillColor : (ShapeColor) c
{
fillcolor = c;
} //setFillColor
- (void) setBounds : (ShapeRect) b
{
bounds = b;
} //setBounds
- (void) draw {
NSLog (@"drawing Circle at (%d %d %d %d) in %@",
bounds.x,
bounds.y,
bounds.height,
colorName(fillColor)
)
} //draw
@end //Triangle
接下来编辑main()
函数来创建三角形。
int main() {
id shape[4]; //修改shapes数组的大小为4
ShapeRect rect0 = {0, 0, 10, 30};
shapes[0] = [Circle new];
[shapes[0] setBounds: rect0];
[shapes[0] setFillColor: RedColor];
ShapeRect rect1 = {30, 40, 50, 60};
shapes[1] = [Rectangle new];
[shapes[1] setBounds: rect1];
[shapes[1] setFillColor: GreenColor];
ShapeRect rect2 = {15, 19, 37, 29};
shapes[2] = [Egg new];
[shapes[2] setBounds: rect2];
[shapes[2] setFillColor: BlueColor];
//创建Triangle对象
ShapeRect rect3 = {47, 32, 80, 50};
shapes[3] = [Triangle new];
[shapes[3] setBounds: rect3];
[shapes[3] setFillColor: RedColor];
drawShapes(shapes,4);
return 0;
} //main
这里没有改变drawShapes()
函数或任何其他处理形状的函数。这就是面向对象的强大之处。