- Objective-C 基础教程第五章,复合
- 什么是复合?
- Car程序
- 自定义NSLog()
- 存取方法get Set
- Tires(轮胎) 存取方法
- Car类代码的其他变化
- 扩展Car程序
- 复合还是继承
- 小结
- 什么是复合?
编程中的复合(composition
)就好像音乐中的作曲(composition
)一样:将多个组件组合在一起,配合使用,从而得到完整的作品。
接下来我们不再用shape作为例子来写代码了,这次用car作为例子写代码,我们先来看看如何搭建汽车模型。
1辆汽车只需要1台发动机和4个轮胎。
mainCarParts.m
#import <Foundation/Foundatin.h>
/*
汽车轮胎
*/
@interface Tire : NSObject
@end
@implementation Tire
- (NSString*) description{
return (@"I' am a tire. I last a while.");
}
@end
/*
汽车发动机
*/
@interface Engine : NSObject
@end
@implementation Engine
- (NSString*) description{
return (@"I am an engine. Vroom!");
}
@end
/*
* 汽车
*/
@interface Car: NSObject
{
//复合概念
Engine *engine;
Tire *tires[4]; //因为engine和tires是Car类的实例变量,所以他们是复合的。
//你可以说 汽车是由4个轮胎和一个发动机组成的。(但是人们一般不会这么说)
} //可以这么说 汽车有4个轮胎和一个发动机
-(void) print;
@end
@implementation Car//Car的init方法创建了4个新轮胎 并赋值给tires数组,还创建了一台发送机并赋值给engine实例变量。
-(id) init{
if(self == [super init]){
engine = [Engine new];
tires[0] = [Tire new];
tires[1] = [Tire new];
tires[2] = [Tire new];
tires[3] = [Tire new];
}
return (self)
}
-(void) print
{
NSLog(@"%@",engine);
for(int i=0;i<4;i++)
{
NSLog(@"%@",tires[i]);
}
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Car *car = [Car new];
[car print];
}
return 0;
}
Tips:(如果类中没有包含任何实例变量,便可以省略掉接口定义中的花括号。)
NSLog可以通过%@
来输出对象。NSLog处理%@
说明符时,会询问参数列表中相应的对象以得到这个对象的描述。
技术原理上是NSLog给这个对象发送了description消息,然后对象的description方法生成了一个NSString并将其返回。
在类中提供description方法就可以自定义NSLog()会如何输出对象。
编程人员很少会对自己编写的程序感到满意,因为软件开发是永无止境的,永远有bug要去修正。
我们可以使用存取方法来改进它,使它的代码更灵活。
存取(accessor)
方法是用来读取或改变某个对象属性的方法。
settter
存取方法之存方法。
getter
存取方法之取方法。
我们在写代码的时候最好不要直接改变类实例变量的值,比如在main函数中car->engine改变值,应该使用setter方法改变,这也算是间接工作的一个例子。
@interface Car:NSObject
{
Engine *engine;
Tire *tires[4];
}
-(Engine *)engine;
-(void) SetEngine:(Engine *) newEngine;
-(Tire*) tireAtIndex:(int) index;
-(void) SetTire:(Tire*) tire atIndex:(int) index;
-(void) print;
@end
@implementation Car
- (Engine*) engine
{
return (engine);
}
-(void) SetEngine:(Engine*) newEngine
{
engine = newEngine;
}
@end
//getter方法engine返回实例变量engine的当前值,Objective-C中所有对象间的交互都是通过指针实现的。
//setter方法让engine指针 指向了newEngine,
int main()
{
Engine *engine = [Engine new];
[car SetEngine:engine];
NSLog(@"the car's engine is %@",[car engine]);
}
Tires(轮胎) 存取方法
-(void) setTire:(Tire*) tire atIndex:(int) index;
-(Tire*) tireAtIndex:(int) index;
由于汽车的4个轮胎都有自己不同的位置,所以Car对象中包含一个轮胎的数组。
在这里我们需要用索引存取器而不能直接访问tires数组。
- (void)setTire:(Tire*) tire atIndex:(int) index
{
if( index < 0 || index > 3)
{
NSLog(@"bad index(%d) in setTire:atIndex:",index);
exit(1);
}
tire[index] = tire;
}
- (Tire*) tireAtIndex:(int) index
{
if( index<0 || index>3)
{
NSLog(@"bad index(%d) in tireAtIndex:",index);
}
return (tires[index]);
}
Tire *tire = [Tire new];
[car setTire:tire atIndex:2];
NSLog(@"tire number two is %@",[car tireAtIndex:2]);
Car类代码的其他变化
Car类的init方法。因为我们有了能够访问engine和tire变量的方法,所以我们不再需要创建这两个变量了,可以直接从外部进行控制,(而是由创建汽车的代码来负责配置发动机和轮胎)
。所以我们可以完全剔除init方法
。
ps:新车的车主会得到一辆没有轮胎和发动机的汽车,不过装配是轻而易举的事(有时软件中的生活比现实生活要容易的多啊