Objective-C 2.0 笔记 (3) Objective-C 物件导向程式设计,类目、协定、继承及复合
2010-05-15 21:43
429 查看
这次要讨论在 Objective-C 可以用哪些方式,来进行物件导向的程式设计。包涵的主题有:
类目(category)
协定(protocol)
继承(inheritance)
复合(composite)
(一)、 使用类目(class category),来扩充现有的类别。
(1) Objective-C 提供一个很方便的机制 class category,让你可以不需用 inheritance,就可以扩充扩充现有的类别。
何时会你会选择用 class category 而不用 inheritance?当你发现,你只需要新增或是修改某些操作(method),你觉得用继承是杀鸡用牛刀。
另外一个情况,是当有不只一个以上的人,来实做 class 的介面,用 class category 可以用来划分分工的内容。
(2) 首先用 Xcode 新增一个专案 OOP1(Object Oriented Program #1),选项如下图:
(2) 接着新增一个类别 Fraction,用来存放一个分数,类别名称就叫 Fraction。
(3) Fraction 类别有两个成员变数, numberator 用来放分子,denominator 用来放分母。用 @property 跟 @synthesize 让 Objective-C 编译器,帮我们产生 get/set 程式码。另外提供 5 个操作,setTo:over: 来设定初始值,add 用来加总两个 Fraction 变数,reduce 用来化简分数,print 用来列印分数,convertToNum 把分数转换成浮点数。
(4) 接着我们透过 class category 来扩充 Fraction,我们要为 Fraction class 新增 add,sub,mul,div,加减乘除四个操作。我们把这个 category 取名为 MathOps。跟新增 Fraction 类别的步骤相同,不过这次我们给的档名为 Fraction+MathOps ,这样的命名是惯例。
(5) 接着就定义及实做 Fraction (MathOps) 这个类目,请参阅下面的程式列表。这个作法,即使你没有 Fraction 类别的原始码的情况下,也适用。这个就是 Objective-C 所提供的弹性。
(6) 接着修改 OOP1 主程式,来测试我们的 Fraction (MathOps) 类目,是否按照我们的设计,进行运算。首先初始化两个 Fraction 变数,fraction1 跟 fraction2,然后一个设为 1/2,另一个设为 1/4,接着进行两个分数的加、减、乘、除。最后释放掉 fraction1 跟 fraction 两个变数。
执行结果如下图:
(二)、 使用协定(protocol),来定义物件支间要如何来互动(interact)。
(1) Objective-C 的 protocol 实质上的意义,就像是 C++ 的 pure virtual class,或是 Java 的 interface。但是取 protocol 这个名称,却更传神。在物件导向的程式设计,有时候你根本不关心跟你互动的是何种物件,你关心的是这个物件,到底听不听得懂你说的话。打个比方,“黑猫白猫,能抓得到老鼠的,就是好猫”,甚至“只要能抓得到老鼠,就是小狗,也不错啊”。
(2) 还是有点搞不清楚 protocol 到底是怎么一回事,对不对?让我们用一个简单的例子,来示范如何使用 protocl。先假设你是一个老板(一个已经存在的物件),你要找一个助手(一个未知的物件),对助手当然会有要求(protocol),只要满足这个要求,你就录用,不然他就不能成为你的助手。
(3) 首先用 Xcode 新增一个专案 OOP2(Object Oriented Program #2)
(2) 接着我们为这个专案新增一个 protocol,名字就叫 AssistantProtocol,名字你可以取你喜欢的,只要你自己明白,这是一个 protocol 档。
(3) AssistantProtocol.h 里定义了合格的 Assistant 应该要会的事情。在这里,我们定义了,一个合格的 Assistant 应该要会 makePlan,updateSchedule,statusReport,然后 bootlick (拍马屁)是选项,并非绝对必要,最后还要 tellTheTruth。
(4) 接着我们新增一个类别 GoodAssistant,这是一个合格的 Assistant,符合 AssistantProtocol 的要求。注意,我们在 GoodAssistant 的介面宣告,用 ,让这个 class 满足 AssistantProtocol 协定。
(5) 然后我们就在 GoodAssist 这个 class 里,实做 AssistantProtocol 所需要的操作。
(6) 接着我们新增一个 BadAssistant 类别,这个类别不满足 AssistantProtocol。
(7) 接着我们新增一个 Boss 类别,这个 Boss 就是要按照 AssistantProtocol 的协定,来伺候的大爷。通常 protocol 协定,就是为这些 Boss 大爷而定的(不是为了 Assistant 助手这些小咖),这点抓到了,你大概就知道 protocol 是怎么一回事。Boss 他根据 AssistantProtocol 可以判断来服务的物件,是否够格,如果够格,就给他们点事情做。
(8) 最后来看主程式 OOP2 是如何用这些物件的,首先初始化一个 Boss 物件,一个 GoodAssistant 物件,及一个 BadAssistant 物件。接着让 boss 物件检查两个 assistant 物件,如果满足 AssistantProtocol 协定,就让物件做点事,如果不合格,就印出错误讯息。
(9) 最后看看执行的结果
(三)、 使用继承(inheritance),站在巨人的肩膀上,写程式。
(1) 继承(inheritance)是物件导向程式设计,常用的一种实做的方法。你可以这样想,有许多事先已经写好的类别,来处理各式各样的事情,有时候直接拿来用,就对了。但是有时候,这些现有的类别,就是少那么一点什么,或是有些地方,跟你要的结果不一样。这时候你可以继承那些类别,新增或是修改你要的部份。这样对比你全部自己写还快。另外,有时候有两个或是两个以上的类别,他们有许多的程式码,是重复而可以互相共用的,这时候把共用的部份,抽出来,变成一个父类别,不但可以让程式码变小,还可减少错误发生的机会。
(2) 用 Xcode 新增一个专案 OOP3 (Object Oriented Program #3),然后新增 Rectangle 类别,有两个成员变数 width,跟 height。另外有三个操作 setWidth:andHeight: 用来设四角形的宽跟高,area 用来求四边形的面积,perimeter 用来求四边形的周长。
(3) 接着新增一个类别 Square 继承 Rectangle,但是多了 setSide 来设正方形的边长,及 side 来取得正方形的边长。
(4) 在主程式 OOP3 中,我们初始化了一个四边形 5 x 10,及一个正方形 10 x 10,然后列印出场方形,及正方形的面积及周长。
(5) 最后是执行的结果:
(四)、 使用复合(composite),就像组合乐高积木,让写程式更简单。
(1) 复合(composite)其实不是 Objective-C 的语言特色,而是物件导向程式设计,用来化繁为简的一个作法。想像有一天,你有一个类别,继承了超过 10 个以上的子类别,会发生什么事情?
首先,各个类别的成员变数(member variable)的名字有可能会重复?各个类别的操作(method)的名字会重复?
接着,因为继承了很多类别,你的成员变数,跟操作的数量,是不是就很多,光是找名字,就累人了。当这种情形发生,用 composite 可以把类别阶层打平,方便管理。
composite 的作法,还有一个优势,就是你可以在执行时在合成你的物件,而继承是你在定义你的类别时,就已经决定。composite 在实际应用上,用得很广泛。
(2) 实际的例子如下,首先用 Xcode 建立一个新的专案 OOP4 (Object Oriented Program #4),然后新增一个类别 Rectangle 如以下列表。
(3) 接着我们新增一个类别 Square,跟之前版本不一样,这次的类别 Square 并没有继承 Rectangle,而是改成用一个成员变数 rectangle 来保存一个 Rectangle 类别的 instance 指标。特别注意,我们覆写了 init,在 init 中,我们初始化了 rectangle 变数。同时,我们也覆写了 dealloc,当 square 被释放前,用来释放掉我们所创建的 rectangle 变数。
(4) 特别注意,Square 类别,在计算面积,及计算周长时,是透过内部的 rectangle 物件,来计算的。你或许会觉得这是多此一举,用继承的方式,实做上似乎比较单纯。但是这是因为我所举的例子,所用到的类别 Rectangle 太简单了。以 composite 的方式,无论多复杂的物件,大概都是这样,依样画葫芦,就算有 100 个类别也是一样。同时,你只需要提供有用到的操作,没用到的可以忽略。
(5) 最后来看执行的结果。
未完待续,下次来谈一谈 Foundation……
类目(category)
协定(protocol)
继承(inheritance)
复合(composite)
(一)、 使用类目(class category),来扩充现有的类别。
(1) Objective-C 提供一个很方便的机制 class category,让你可以不需用 inheritance,就可以扩充扩充现有的类别。
何时会你会选择用 class category 而不用 inheritance?当你发现,你只需要新增或是修改某些操作(method),你觉得用继承是杀鸡用牛刀。
另外一个情况,是当有不只一个以上的人,来实做 class 的介面,用 class category 可以用来划分分工的内容。
(2) 首先用 Xcode 新增一个专案 OOP1(Object Oriented Program #1),选项如下图:
(2) 接着新增一个类别 Fraction,用来存放一个分数,类别名称就叫 Fraction。
(3) Fraction 类别有两个成员变数, numberator 用来放分子,denominator 用来放分母。用 @property 跟 @synthesize 让 Objective-C 编译器,帮我们产生 get/set 程式码。另外提供 5 个操作,setTo:over: 来设定初始值,add 用来加总两个 Fraction 变数,reduce 用来化简分数,print 用来列印分数,convertToNum 把分数转换成浮点数。
1: //
2: // Fraction.h
3:// OOP1
4: //
5: // Created by Chou Shunyuan on 5/15/10.
6:// Copyright 2010 __MyCompanyName__. All rights reserved.
7://
8:
9: #import
10:
11:
12:@interface Fraction : NSObject
13:{
14:int numerator;
15: int denominator;
16:}
17:
18:@property int numerator;
19:@property int denominator;
20:
21:-(void) setTo: (int)n over: (int) d;
22:-(Fraction*) add: (Fraction*) f;
23:-(void) reduce;
24:-(double) convertToNum;
25:-(void) print;
26:
27:@end
28:
1: //
2: // Fraction.m
3:// OOP1
4: //
5: // Created by Chou Shunyuan on 5/15/10.
6:// Copyright 2010 __MyCompanyName__. All rights reserved.
7://
8:
9: #import"Fraction.h"
10:
11:
12:@implementation Fraction
13:
14:@synthesize numerator, denominator;
15:
16:-(void) setTo: (int) n over: (int) d
17:{
18:numerator = n;
19:denominator = d;
20:}
21:
22:-(Fraction*) add: (Fraction*) f
23:{
24://
25: // (a/b) + (c/d) = ((a*d) + (b*c)) / (b*d)
26://
27:
28:Fraction *result = [[Fraction alloc] init];
29:
30: result.numerator = (self.numerator * f.denominator) + (self.denominator * f.numerator);
31:result.denominator = (self.denominator * f.denominator);
32:
33:[result reduce];
34:
35: return result;
36:}
37:
38:-(void) reduce
39:{
40: int u = numerator;
41: int v = denominator;
42: int temp;
43:
44: while (v != 0)
45:{
46: temp = u % v;
47: u = v;
48:v = temp;
49: }
50:
51: numerator /= u;
52:denominator /= u;
53:}
54:
55:-(double) convertToNum
56:{
57: double result = (double) numerator / denominator;
58:
59:return result;
60: }
61:
62: -(void) print
63:{
64:NSLog(@" %i/%i", numerator, denominator);
65: }
66:
67:@end
68:
(4) 接着我们透过 class category 来扩充 Fraction,我们要为 Fraction class 新增 add,sub,mul,div,加减乘除四个操作。我们把这个 category 取名为 MathOps。跟新增 Fraction 类别的步骤相同,不过这次我们给的档名为 Fraction+MathOps ,这样的命名是惯例。
(5) 接着就定义及实做 Fraction (MathOps) 这个类目,请参阅下面的程式列表。这个作法,即使你没有 Fraction 类别的原始码的情况下,也适用。这个就是 Objective-C 所提供的弹性。
1: //
2: // Fraction+MathOps.h
3:// OOP1
4: //
5: // Created by Chou Shunyuan on 5/15/10.
6:// Copyright 2010 __MyCompanyName__. All rights reserved.
7://
8:
9: #import"Fraction.h"
10:
11:
12:@interface Fraction (MathOps)
13:
14:-(Fraction*) add: (Fraction*) f;
15:-(Fraction*) sub: (Fraction*) f;
16:-(Fraction*) mul: (Fraction*) f;
17:-(Fraction*) div: (Fraction*) f;
18:
19:@end
20:
1: //
2: // Fraction+MathOps.m
3:// OOP1
4: //
5: // Created by Chou Shunyuan on 5/15/10.
6:// Copyright 2010 __MyCompanyName__. All rights reserved.
7://
8:
9: #import"Fraction+MathOps.h"
10:
11:
12:@implementation Fraction (MathOps)
13:
14:-(Fraction*) add: (Fraction*) f
15:{
16:// (a/b) + (c/d) = ((a*d) + (b*c)) / (b*d)
17:
18:Fraction *result = [[Fraction alloc] init];
19:
20:result.numerator = (self.numerator * f.denominator) + (self.denominator * f.numerator);
21:result.denominator = self.denominator * f.denominator;
22:
23:[result reduce];
24:
25: return result;
26:}
27:
28:-(Fraction*) sub: (Fraction*) f
29:{
30: // (a/b) - (c/d) = ((a*d) - (b*c)) / (b*d)
31:
32:Fraction *result = [[Fraction alloc] init];
33:
34:result.numerator = (self.numerator * f.denominator) - (self.denominator * f.numerator);
35: result.denominator = self.denominator * f.denominator;
36:
37: [result reduce];
38:
39: return result;
40:}
41:
42: -(Fraction*) mul: (Fraction*) f
43:{
44: // (a/b) * (c/d) = (a*c) / (b*d)
45:
46: Fraction *result = [[Fraction alloc] init];
47:
48:result.numerator = (self.numerator * f.numerator);
49: result.denominator = self.denominator * f.denominator;
50:
51: [result reduce];
52:
53: return result;
54:}
55:
56:-(Fraction*) div: (Fraction*) f
57:{
58:// (a/b) / (c/d) = (a*d) / (b*c)
59:
60: Fraction *result = [[Fraction alloc] init];
61:
62: result.numerator = (self.numerator * f.denominator);
63: result.denominator = self.denominator * f.numerator;
64:
65: [result reduce];
66:
67: return result;
68:}
69:
70: @end
71:
(6) 接着修改 OOP1 主程式,来测试我们的 Fraction (MathOps) 类目,是否按照我们的设计,进行运算。首先初始化两个 Fraction 变数,fraction1 跟 fraction2,然后一个设为 1/2,另一个设为 1/4,接着进行两个分数的加、减、乘、除。最后释放掉 fraction1 跟 fraction 两个变数。
1: #import"Fraction.h"
2: #import "Fraction+MathOps.h"
3:
4: int main (int argc, const char * argv[])
5: {
6: NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
7:
8:Fraction *fraction1 = [[Fraction alloc] init];
9: Fraction *fraction2 = [[Fraction alloc] init];
10:Fraction *result;
11:
12: [fraction1 setTo: 1 over: 2];
13:[fraction2 setTo: 1 over: 4];
14:
15: // compute (1/2) + (1/4)
16:result = [fraction1 add: fraction2];
17:[result print];
18:[result release];
19:
20:// compute (1/2) - (1/4)
21:result = [fraction1 sub: fraction2];
22:[result print];
23:[result release];
24:
25: // compute (1/2) * (1/4)
26:result = [fraction1 mul: fraction2];
27:[result print];
28:[result release];
29:
30: // compute (1/2) / (1/4)
31:result = [fraction1 div: fraction2];
32:[result print];
33:[result release];
34:
35: [fraction1 release];
36:[fraction2 release];
37:
38:[pool drain];
39: return 0;
40:}
41:
执行结果如下图:
(二)、 使用协定(protocol),来定义物件支间要如何来互动(interact)。
(1) Objective-C 的 protocol 实质上的意义,就像是 C++ 的 pure virtual class,或是 Java 的 interface。但是取 protocol 这个名称,却更传神。在物件导向的程式设计,有时候你根本不关心跟你互动的是何种物件,你关心的是这个物件,到底听不听得懂你说的话。打个比方,“黑猫白猫,能抓得到老鼠的,就是好猫”,甚至“只要能抓得到老鼠,就是小狗,也不错啊”。
(2) 还是有点搞不清楚 protocol 到底是怎么一回事,对不对?让我们用一个简单的例子,来示范如何使用 protocl。先假设你是一个老板(一个已经存在的物件),你要找一个助手(一个未知的物件),对助手当然会有要求(protocol),只要满足这个要求,你就录用,不然他就不能成为你的助手。
(3) 首先用 Xcode 新增一个专案 OOP2(Object Oriented Program #2)
(2) 接着我们为这个专案新增一个 protocol,名字就叫 AssistantProtocol,名字你可以取你喜欢的,只要你自己明白,这是一个 protocol 档。
(3) AssistantProtocol.h 里定义了合格的 Assistant 应该要会的事情。在这里,我们定义了,一个合格的 Assistant 应该要会 makePlan,updateSchedule,statusReport,然后 bootlick (拍马屁)是选项,并非绝对必要,最后还要 tellTheTruth。
1: //
2: // AssistantProtocol.h
3:// OOP2
4: //
5: // Created by Chou Shunyuan on 5/15/10.
6:// Copyright 2010 __MyCompanyName__. All rights reserved.
7://
8:
9: #import
10:
11:
12:@protocol AssistantProtocol
13:
14:-(void) makePlan;
15:-(void) updateSchedule;
16:-(void) statusReport;
17:
18:@optional
19:-(void) bootlick;
20:
21:@required
22:-(void) tellTheTruth;
23:
24:@end
25:
(4) 接着我们新增一个类别 GoodAssistant,这是一个合格的 Assistant,符合 AssistantProtocol 的要求。注意,我们在 GoodAssistant 的介面宣告,用 ,让这个 class 满足 AssistantProtocol 协定。
(5) 然后我们就在 GoodAssist 这个 class 里,实做 AssistantProtocol 所需要的操作。
1: //
2: // GoodAssistant.h
3:// OOP2
4: //
5: // Created by Chou Shunyuan on 5/15/10.
6:// Copyright 2010 __MyCompanyName__. All rights reserved.
7://
8:
9: #import
10:#import "AssistantProtocol.h"
11:
12:@interface GoodAssistant : NSObject
13:{
14:// nothing special here
15:}
16:
17:@end
18:
1: //
2: // GoodAssistant.m
3:// OOP2
4: //
5: // Created by Chou Shunyuan on 5/15/10.
6:// Copyright 2010 __MyCompanyName__. All rights reserved.
7://
8:
9: #import"GoodAssistant.h"
10:
11:
12:@implementation GoodAssistant
13:
14:-(void) makePlan
15:{
16:NSLog(@"make plan");
17:}
18:
19:-(void) updateSchedule
20:{
21:NSLog(@"update schedule");
22:}
23:
24:-(void) statusReport
25:{
26:NSLog(@"status report");
27:}
28:
29:-(void) bootlick
30: {
31:NSLog(@"bootlick");
32: }
33:
34: -(void) tellTheTruth
35:{
36:NSLog(@"tell the truth");
37:}
38:
39:@end
40:
(6) 接着我们新增一个 BadAssistant 类别,这个类别不满足 AssistantProtocol。
1: //
2: // BadAssistant.h
3:// OOP2
4: //
5: // Created by Chou Shunyuan on 5/15/10.
6:// Copyright 2010 __MyCompanyName__. All rights reserved.
7://
8:
9: #import
10:
11:
12:@interface BadAssistant : NSObject
13:{
14:// nothing special here
15:}
16:
17:@end
18:
1: //
2: // BadAssistant.m
3:// OOP2
4: //
5: // Created by Chou Shunyuan on 5/15/10.
6:// Copyright 2010 __MyCompanyName__. All rights reserved.
7://
8:
9: #import"BadAssistant.h"
10:
11:@implementation BadAssistant
12:
13:@end
14:
(7) 接着我们新增一个 Boss 类别,这个 Boss 就是要按照 AssistantProtocol 的协定,来伺候的大爷。通常 protocol 协定,就是为这些 Boss 大爷而定的(不是为了 Assistant 助手这些小咖),这点抓到了,你大概就知道 protocol 是怎么一回事。Boss 他根据 AssistantProtocol 可以判断来服务的物件,是否够格,如果够格,就给他们点事情做。
1: //
2: // Boss.h
3:// OOP2
4: //
5: // Created by Chou Shunyuan on 5/15/10.
6:// Copyright 2010 __MyCompanyName__. All rights reserved.
7://
8:
9: #import
10:#import "AssistantProtocol.h"
11:
12:
13:@interface Boss : NSObject
14:{
15: // nothing special here
16:}
17:
18:-(BOOL) IsAssistantQualified: (id) assistant;
19:-(void) AskAssistantToWork: (id) asssitant;
20:
21:@end
22:
1: //
2: // Boss.m
3:// OOP2
4: //
5: // Created by Chou Shunyuan on 5/15/10.
6:// Copyright 2010 __MyCompanyName__. All rights reserved.
7://
8:
9: #import"Boss.h"
10:
11:
12:@implementation Boss
13:
14:-(BOOL) IsAssistantQualified: (id) assistant
15:{
16:return [assistant conformsToProtocol: @protocol(AssistantProtocol)];
17:}
18:
19:-(void) AskAssistantToWork: (id) asssitant
20:{
21:[asssitant makePlan];
22:[asssitant updateSchedule];
23:[asssitant statusReport];
24:[asssitant bootlick];
25: [asssitant tellTheTruth];
26:}
27:
28:@end
29:
(8) 最后来看主程式 OOP2 是如何用这些物件的,首先初始化一个 Boss 物件,一个 GoodAssistant 物件,及一个 BadAssistant 物件。接着让 boss 物件检查两个 assistant 物件,如果满足 AssistantProtocol 协定,就让物件做点事,如果不合格,就印出错误讯息。
1: #import
2: #import "AssistantProtocol.h"
3:#import "GoodAssistant.h"
4: #import "BadAssistant.h"
5: #import "Boss.h"
6:
7:int main (int argc, const char * argv[])
8:{
9: NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
10:
11:Boss *boss = [[Boss alloc] init];
12: GoodAssistant *assistant1 = [[GoodAssistant alloc] init];
13:BadAssistant *assistant2 = [[BadAssistant alloc] init];
14:
15: NSLog(@"-- if assistant1 is qualified then ask it to work --");
16:
17:if ([boss IsAssistantQualified: assistant1] == YES)
18:[boss AskAssistantToWork: assistant1];
19:else
20:NSLog(@"assistant1 is not qualified.");
21:
22:NSLog(@"-- if assistant2 is qualified then ask it to work --");
23:
24:if ([boss IsAssistantQualified: assistant2] == YES)
25: [boss AskAssistantToWork: assistant2];
26:else
27:NSLog(@"assistant2 is not qualified.");
28:
29:[boss release];
30: [assistant1 release];
31:[assistant2 release];
32:
33:[pool drain];
34: return 0;
35:}
36:
(9) 最后看看执行的结果
(三)、 使用继承(inheritance),站在巨人的肩膀上,写程式。
(1) 继承(inheritance)是物件导向程式设计,常用的一种实做的方法。你可以这样想,有许多事先已经写好的类别,来处理各式各样的事情,有时候直接拿来用,就对了。但是有时候,这些现有的类别,就是少那么一点什么,或是有些地方,跟你要的结果不一样。这时候你可以继承那些类别,新增或是修改你要的部份。这样对比你全部自己写还快。另外,有时候有两个或是两个以上的类别,他们有许多的程式码,是重复而可以互相共用的,这时候把共用的部份,抽出来,变成一个父类别,不但可以让程式码变小,还可减少错误发生的机会。
(2) 用 Xcode 新增一个专案 OOP3 (Object Oriented Program #3),然后新增 Rectangle 类别,有两个成员变数 width,跟 height。另外有三个操作 setWidth:andHeight: 用来设四角形的宽跟高,area 用来求四边形的面积,perimeter 用来求四边形的周长。
1: //
2: // Rectangle.h
3:// OOP3
4: //
5: // Created by Chou Shunyuan on 5/15/10.
6:// Copyright 2010 __MyCompanyName__. All rights reserved.
7://
8:
9: #import
10:
11:
12:@interface Rectangle : NSObject
13:{
14:double width;
15: double height;
16:}
17:
18:@property double width;
19:@property double height;
20:
21:-(void) setWidth: (double) w andHeight: (double) h;
22:-(double) area;
23:-(double) perimeter;
24:
25:@end
26:
1: //
2: // Rectangle.m
3:// OOP3
4: //
5: // Created by Chou Shunyuan on 5/15/10.
6:// Copyright 2010 __MyCompanyName__. All rights reserved.
7://
8:
9: #import"Rectangle.h"
10:
11:
12:@implementation Rectangle
13:
14:@synthesize width, height;
15:
16:-(void) setWidth: (double) w andHeight: (double) h
17:{
18:width = w;
19:height = h;
20:}
21:
22:-(double) area
23:{
24:return (width * height);
25:}
26:
27:-(double) perimeter
28:{
29:return (2 * (width + height));
30: }
31:
32: @end
33:
(3) 接着新增一个类别 Square 继承 Rectangle,但是多了 setSide 来设正方形的边长,及 side 来取得正方形的边长。
1: //
2: // Square.h
3:// OOP3
4: //
5: // Created by Chou Shunyuan on 5/15/10.
6:// Copyright 2010 __MyCompanyName__. All rights reserved.
7://
8:
9: #import"Rectangle.h"
10:
11:
12:@interface Square : Rectangle
13:{
14:
15:}
16:
17:-(void) setSide: (double) side;
18:-(double) side;
19:
20:@end
21:
1: //
2: // Square.m
3:// OOP3
4: //
5: // Created by Chou Shunyuan on 5/15/10.
6:// Copyright 2010 __MyCompanyName__. All rights reserved.
7://
8:
9: #import"Square.h"
10:
11:
12:@implementation Square
13:
14:-(void) setSide: (double) side
15:{
16:[self setWidth: side andHeight: side];
17:}
18:
19:-(double) side
20:{
21:return width;
22:}
23:
24:@end
25:
(4) 在主程式 OOP3 中,我们初始化了一个四边形 5 x 10,及一个正方形 10 x 10,然后列印出场方形,及正方形的面积及周长。
1: #import"Rectangle.h"
2: #import "Square.h"
3:
4: int main (int argc, const char * argv[])
5: {
6: NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
7:
8:Rectangle *rectangle = [[Rectangle alloc] init];
9: Square *square = [[Square alloc] init];
10:
11:[rectangle setWidth: 5.0 andHeight: 10.0];
12: NSLog(@"the area of %g x %g rectangle = %g",
13:rectangle.width,
14:rectangle.height,
15: [rectangle area]);
16:
17:NSLog(@"the perimeter of %g x %g rectangle = %g",
18:rectangle.width,
19:rectangle.height,
20: [rectangle perimeter]);
21:
22:[square setSide: 10.0];
23:NSLog(@"the area of %g x %g square = %g",
24:[square side],
25: [square side],
26: [square area]);
27:
28:NSLog(@"the perimeter of %g x %g square = %g",
29:[square side],
30: [square side],
31: [square perimeter]);
32:
33:[rectangle release];
34:[square release];
35:
36:[pool drain];
37:return 0;
38:}
39:
(5) 最后是执行的结果:
(四)、 使用复合(composite),就像组合乐高积木,让写程式更简单。
(1) 复合(composite)其实不是 Objective-C 的语言特色,而是物件导向程式设计,用来化繁为简的一个作法。想像有一天,你有一个类别,继承了超过 10 个以上的子类别,会发生什么事情?
首先,各个类别的成员变数(member variable)的名字有可能会重复?各个类别的操作(method)的名字会重复?
接着,因为继承了很多类别,你的成员变数,跟操作的数量,是不是就很多,光是找名字,就累人了。当这种情形发生,用 composite 可以把类别阶层打平,方便管理。
composite 的作法,还有一个优势,就是你可以在执行时在合成你的物件,而继承是你在定义你的类别时,就已经决定。composite 在实际应用上,用得很广泛。
(2) 实际的例子如下,首先用 Xcode 建立一个新的专案 OOP4 (Object Oriented Program #4),然后新增一个类别 Rectangle 如以下列表。
1: //
2: // Rectangle.h
3:// OOP4
4: //
5: // Created by Chou Shunyuan on 5/15/10.
6:// Copyright 2010 __MyCompanyName__. All rights reserved.
7://
8:
9: #import
10:
11:
12:@interface Rectangle : NSObject
13:{
14:double width;
15:double height;
16:}
17:
18:@property double width;
19:@property double height;
20:
21:-(void) setWidth: (double) w andHeight: (double) h;
22:-(double) area;
23:-(double) perimeter;
24:
25:@end
26:
1: //
2: // Rectangle.m
3:// OOP4
4: //
5: // Created by Chou Shunyuan on 5/15/10.
6:// Copyright 2010 __MyCompanyName__. All rights reserved.
7://
8:
9: #import"Rectangle.h"
10:
11:
12:@implementation Rectangle
13:
14:@synthesize width, height;
15:
16:-(void) setWidth: (double) w andHeight: (double) h
17:{
18:width = w;
19:height = h;
20:}
21:
22:-(double) area
23:{
24:return (width * height);
25:}
26:
27:-(double) perimeter
28:{
29:return (2 * (width + height));
30: }
31:
32: @end
33:
(3) 接着我们新增一个类别 Square,跟之前版本不一样,这次的类别 Square 并没有继承 Rectangle,而是改成用一个成员变数 rectangle 来保存一个 Rectangle 类别的 instance 指标。特别注意,我们覆写了 init,在 init 中,我们初始化了 rectangle 变数。同时,我们也覆写了 dealloc,当 square 被释放前,用来释放掉我们所创建的 rectangle 变数。
1: //
2: // Square.h
3:// OOP4
4: //
5: // Created by Chou Shunyuan on 5/15/10.
6:// Copyright 2010 __MyCompanyName__. All rights reserved.
7://
8:
9: #import
10:#import "Rectangle.h"
11:
12:
13:@interface Square : NSObject
14:{
15:Rectangle *rectangle;
16:}
17:
18:-(id) init;
19:-(void) dealloc;
20:
21:-(void) setSide: (double) side;
22:-(double) side;
23:
24:-(double) area;
25:-(double) perimeter;
26:
27:@end
28:
1: //
2: // Square.m
3:// OOP4
4: //
5: // Created by Chou Shunyuan on 5/15/10.
6:// Copyright 2010 __MyCompanyName__. All rights reserved.
7://
8:
9: #import"Square.h"
10:
11:
12:@implementation Square
13:
14:-(id)init
15:{
16:if (self = [super init])
17:{
18:rectangle = [[Rectangle alloc] init];
19:}
20:
21:return self;
22:}
23:
24:-(void) dealloc
25:{
26:[rectangle release];
27:[super dealloc];
28:}
29:
30: -(void) setSide: (double) side
31:{
32:if (rectangle)
33:{
34: [rectangle setWidth: side andHeight: side];
35: }
36:}
37:
38:-(double) side
39:{
40: if (rectangle)
41: {
42: return rectangle.width;
43:}
44:
45:return 0;
46:}
47:
48:-(double) area
49: {
50:if (rectangle)
51: {
52: return [rectangle area];
53: }
54:
55: return 0;
56:}
57:
58:-(double) perimeter
59: {
60: if (rectangle)
61: {
62: return [rectangle perimeter];
63: }
64:
65: return 0;
66:}
67:
68:@end
69:
(4) 特别注意,Square 类别,在计算面积,及计算周长时,是透过内部的 rectangle 物件,来计算的。你或许会觉得这是多此一举,用继承的方式,实做上似乎比较单纯。但是这是因为我所举的例子,所用到的类别 Rectangle 太简单了。以 composite 的方式,无论多复杂的物件,大概都是这样,依样画葫芦,就算有 100 个类别也是一样。同时,你只需要提供有用到的操作,没用到的可以忽略。
(5) 最后来看执行的结果。
未完待续,下次来谈一谈 Foundation……
相关文章推荐
- Objective-C 2.0 笔记 (2) Objective-C 程式,有多个模组,及动态系结
- objective-C学习笔记(六)继承与多态
- [阅读笔记]面向对象的设计法则--法则1:优先使用(对象)组合,而非(类)继承 Favor Composition Over Inheritance
- 设计模式:复合模式(学习笔记)
- Javascript 设计模式学习笔记(2) - 继承(Inheritance) (上)
- 【程序设计实习】笔记 6--001继承
- Objective-C学习笔记十:继承二
- about face2.0——第一章(4):目标导向设计过程
- [转载]Objective-C中的继承与复合技术
- Objective-C基础教程2(继承和复合)
- Objective-C 2.0 with Cocoa Foundation--- 4,继承
- Objective-C学习笔记第五章复合
- objective 2.0 内存管理机制 笔记
- iOS开发笔记--Objective-C实现多继承
- Objective-C基础学习笔记(四)-面向对象的三大特性之继承
- <<Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法>>笔记-熟悉Objective-C
- Objective-C 2.0 with Cocoa Foundation--- 4,继承
- iOS学习笔记-Objective-C-继承和实例化、对象初始化
- Learn Objective‐C,学习笔记03 类别(实现多重继承的方法)
- Objective-C学习笔记--复合