8.2 通过继承扩展—添加新方法

继承通常用于扩展一个类。举个例子,假设你刚接受一项任务:开发一些处理2D图形对象(如:矩形、圆形和三角形)的类。现在,只考虑矩形。实际上,回顾第4章“数据类型和表达式”的练习7,从以下例子开始@interface部分:


@interface Rectangle:NSObject

{

int width;

int height;

}

@property int width, height;

-(int)area;

-(int)perimeter;

@end


当前的方法可以设置矩形的宽与高,返回它们的值并计算其面积与周长。再添加一个方法,它允许你使用相同的消息调用来设置矩形的宽度与长度值,如下:


-(void)setWidth:(int)w andHeight:(int)h;


假设将新类声明键入名为Rectangle.h的文件。实现文件Rectangle.m将如下面所示:


import“Rectangle.h”

@implementation Rectangle

@synthesize width, height;

-(void)setWidth:(int)w andHeight:(int)h

{

width=w;

heighe=h;

}

-(int)area

{

return width*height;

}

-(int)perimeter

{

return(width+height)*2;

}

@end


每个方法定义都很直观。代码清单8-2显示了一个测试它的main例程。

代码清单8-2


import“Rectangle.h”

int main(int argc, char*argv[])

{

NSAutoreleasePool*pool=[[NSAutoreleasePool alloc]init];

Rectangle*myRect=[[Rectangle alloc]init];

[myRect setWidth:5 andHeight:8];

NSLog(@“Rectangle:w=%i, h=%i”,

myRect.width, myRect.height);

NSLog(@“Area=%i, Perimeter=%i”,

[myRect area],[myRect perimeter]);

[myRect release];

[pool drain];

return 0;

}


代码清单8-2输出结果


Rectangle:w=5,h=8

Area=40,Perimeter=26


给myRect分配内存并将其初始化,然后将其宽设为5、高设为8。输出的第一行验证了这一点。然后,使用合适的消息调用,计算矩形的面积和周长,返回值由NSLog进行显示。

处理矩形之后,假设现在需要处理正方形。可以定义一个名为Square的新类,并在其中定义同Rectangle类相似的方法。或者,认识到正方形只是长方形的特例,长和宽恰好相同的长方形。

因此,简单的处理方法就是定义一个名为Square的新类,并使它成为Rectangle的子类。这样除了定义自己的方法和变量之外,可以使用Rectangle类中的所有方法和变量。现在,可能要添加的唯一方法可能是将正方形的边设置特定的值,并检索该值。代码清单8-3显示了Square类的接口文件和实现文件。

代码清单8-3 Square.h接口文件


import“Rectangle.h”

@interface Square:Rectangle

-(void)setSide:(int)s;

-(int)side;

@end


代码清单8-3 Square.m实现文件


import“Square.h”

@implementation Square:Rectangle;

-(void)setSide:(int)s

{

[self setWidth:s andHeight:s];

}

-(int)side

{

return width;

}

@end


注意此处所做的工作。你将Square定义为Rectangle的子类,这是在头文件Rectangle.h中声明的。这里不必添加任何实例变量,但是添加了两个名为setSide和side的新方法。

虽然正方形只有一个边,但在内部也可用两个数表示,这样也可以。这对于Square类的用户是隐藏的。如果需要,以后随时可以重新定义Square类。根据前面介绍的封装概念,任何用户都无须担心内部的细节问题。

setSide:方法利用从Rectangle类继承的方法设定矩形宽度与长度的值。所以setSide:调用Rectangle类的setWidth:andHeight:方法,同时传递参数作为宽度与长度值。实际上不必再进行其他任何操作。处理Square对象的人通过利用setSide:方法,可以设置正方形的大小,并利用Rectangle类的方法计算正方形的面积、周长,等等。代码清单8-3展示了新的Square类的测试程序和输出。

代码清单8-3测试程序


import“Square.h”

import<Foundation/Foundation.h>

int main(int argc, char*argv[])

{

NSAutoreleasePool*pool=[[NSAutoreleasePool alloc]init];

Square*mySquare=[[Square alloc]init];

[mySquare setSide:5];

NSLog(@“Square s=%i”,[mySquare side]);

NSLog(@“Area=%i, Perimeter=%i”,

[mySquare area],[mySquare perimeter]);

[mySquare release];

[pool drain];

return 0;

}


代码清单8-3输出结果


Square s=5

Area=25,Perimeter=20


定义Square类的方式是在Objective-C中使用类的基本技巧:扩展自己或其他人以前实现的类,使其适合自己的需要。另外,所谓的分类(category)机制允许你以模块方式向现有类定义添加新方法,也就是,不必经常给同一接口和实现文件增加新定义。当想要对你没有源代码访问权限的类添加新定义时,这特别方便。在第11章“分类和协议”中将学到分类。

8.2.1 Point类和内存分配

Rectangle类只存储矩形大小。在实际的图形应用中,可能需要保存各种附加消息,如:矩形的填充色、线条颜色、窗口中的位置(原点),等等。可以方便地扩展这些类来处理这些情况。现在,处理矩形原点的概念。假设“原点”是指笛卡尔坐标系(x, y)中矩形左下角的位置。如果正在编写绘图应用程序,这一点可能代表矩形在窗口中的位置。如图8-4所示。

8.2 通过继承扩展—添加新方法 - 图1

图 8-4 窗口中绘制的矩形

在图8-4中,矩形的原点是(x1,y1)。

可以扩展Rectangle类,将矩形原点(x, y)保存为两个不同的值。或许你已经认识到在开发图形应用程序的过程中,要处理很多坐标,因此要定义一个名为XYPoint的类(在第3章的练习7中出现过这个问题):


import<Foundation/Foundation.h>

@interface XYPoint:NSObject

{

int x;

int y;

}

@property int x, y;

-(void)setX:(int)xVal andY:(int)yVal;

@end


现在,回到Rectangle类。希望能够存储矩形的原点,所以,必须向Rectangle类的定义添加另一实例变量origin:


@interface Rectangle:NSObject

{

int width;

int height;

XYPoint*origin;

}

……


应该再添加一个方法,设置矩形的原点并对其进行检索。为突出重点,我们这里不综合原点的存取器方法,我们将自行编写。