之前学习的继承,继承是在两个类之间建立的关系的一种方式。现在我们学习类之间关系的另一种建立方式—复合。使用复合可以组合多个对象,使之分工协作。在实际的程序中,会用到同时使用继承和复合来创建自己的类,这时非常重要的。
5.1 什么是复合
编程中的复合是将多个组件组合在一起配合使用,从而得到完成的程序。
定义:复合是通过包含作为实例变量的对象指针实现的。例如:
@interface Car : NSObject{
o Chair *chair;
o Wheel *wheel;
}
一个车Car对象,拥有一个Chair对象和一个Wheel轮子对象,也就是说Chair对象和Wheel对象通过复合的方式组成了Car对象。
注:严格的来讲,只有对象间的组合才能叫做复合。
5.1.1 Car程序
一辆汽车包含了很多的部件,比如轮胎、发动机等等。我们用复合的方式在程序中来描述汽车这个对象。代码CarParts,新建一个Tire和Engine类
5.1.2 自定义NSLog()
通过NSLog()可以使用%@格式说明符来输出对象。NSLog()处理%@说明符时,它会询问参数列表中相应的对象以得到这个对象的描述。
我们定义一个Car类
· @interface Car : NSObject{
o Engine *engine;//发动机对象
o Tire *tires[4];//轮胎对象四个
· }
注:在程序init方法中,有if(self == [super init])判断语句。其意思是:若要超类可以完成所需的一次性初始化,需要调用[super init].init返回的值(id型数据,即泛型对象指针)描述了被初始化的对象。
5.2 存取方法
我们使用存取方法来改进CarParts程序,使它的代码更加的灵活。
1.定义:存取方法( accessor method )是用来读取或改变对象特定属性的方法。
· setter方法:如setFillColor方法就是一个存取方法,称为setter方法,目的是为对象中的某属性赋值。
· 修改方法:是用来改变对象状态的方法的术语
· getter方法:是另一种存取方法,目的是为了读取对象中的某属性的值。
2.修改CarParts程序的Car类,给它添加一些存取方法.
一般的,存取方法总是成对出现的,一个设置属性的值setter,一个读取属性的值getter。
· setter方法根据它所更改的属性的名称来命名,如:setName,setEngine。
· getter方法紧紧根据其返回的属性名称来命名,如:engine,name。
5.2.1 设置发动机的属性
(1).setter和getter方法声明
· -(Engine*)engine;
· -(void)setEngine:(Engine*) newEngine;
(2).setter和getter方法实现
· -(Engine*)engine{
return engine;
· }
· - (void)setEngine:(Engine *)newEngine{
engine = newEngine;
· }
(3).使用方法
· Engine *engine = [Engine new];
· [car setEngine:engine];
· NSLog(@”%@”,[carengine]);
注:在Objective-C中所有对象间的交互都是通过指针实现的。
5.2.2 设置轮胎的属性
tires的存取方法比较复杂
(1). -(void)setTire:(Tire *) tire atIndex:(int) index{
· if(index< 0 || index > 0 ){
o exit(1);
· }
· tires[index] = tire;
· }
(2). -(Tire *) tireAtIndex:(int) index{
· if(index< 0 || index > 0 ){
o exit(1);
· }
· return tires[index];
· }
(3).使用方法
· Tire *tire = [Tire new];
· [car setTire: tire atIndex:2];
· NSLog(@”%@”,[car tireAtIndex:2]);
注意:tire存取方法使用了通用代码来检查实例变量tires数组索引,以保证它是有效数值。该代码就是所谓的防御式编程,它能够在开发周期的早期发现错误。
5.2.3跟踪汽车的变化
既然我们在Car中创建了访问engine和tire的方法,我们就不需要init方法了,所以我们将init方法删除后在main()函数中重新创建他们
· Car *car= [Car new];
· Engine *engine = [Engine new];
· [car setEngine:engine];
·
· for(int i = 0;i < 4;i++){
Tire *tire = [Tire new];
[car setTire:tire atIndex:i];
· }
· [car print];
5.3 扩展CarParts程序
我们创建一个新类继承自Engine,来扩展CarParts程序,参考代码:CarParts
5.4复合还是继承
CarParts同时用到了继承和复合,那么什么时候用继承?什么时候用复合?
· 继承在对象间建立了“is a”(一个)的关系。
· 复合建立的则是“has a”(有一个)的关系。
不能随便用继承和复合,一定考虑到这两个对象的关系,然后恰到好处的使用继承和复合。
小结
复合是面向对象编程中一个非常重要的概念,我们用来创建和引用其他对象的对象
· 存取方法和复合是密不可分的。
· 两种类型的存取方法:setter和getter
Cocoa的存取方法命名规则。注意的是对于返回属性值的存取方法,名称中不能使用get这个词。