天天看点

第五章 复合

   之前学习的继承,继承是在两个类之间建立的关系的一种方式。现在我们学习类之间关系的另一种建立方式—复合。使用复合可以组合多个对象,使之分工协作。在实际的程序中,会用到同时使用继承和复合来创建自己的类,这时非常重要的。

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这个词。