复合 composition

object-c中,复合是通过包含作为实例变量的对象指针实现的。严格的说,只有对象间的组合叫复合。

以汽车模型为例,1辆汽车(Car)需要1台发动机(Engine)和4个轮胎(Tire),这里不去研究真正的轮胎和发动机的物理模型,而仅包含方法的类来输出各自代表的含义:轮胎的对象说它们是轮胎,发动机的对象说它们是发动机。

1 @interface Car : NSObject
2 {
3     Engine *engine;
4     Tyre *tire;
5 }
6 @end //Car

以上代码可以看出,engine和tire是通过复合的方式组成Car。

1 @interface  Engine : NSObject
2 @end //Engine
3 
4 @implementation Engine
5 -(NSString)Descroption
6 {
7   return (@"I am engine. Vroom");
8 } //description
9 @end //Engine

Engine类只有一个description方法

1 @interface Car : NSObject
2 {
3     Engine *engine;
4     Tire *tires[4];
5 }
6 -(void)print;
7 @end//Car

Car 拥有engine对象和一个由4个tire对象组成的数组,通过复合的方式组装自己,Car同时还有一个print方法,该方法使用NSLog()来输出轮胎和发动机的描述。

因为engine和tires是Car的实例变量,所以它们是复合的。

下面我们来看看Car的实现:

 1 @implementation Car
 2 -(id)init
 3 {
 4     if(self = [super init]){
 5       engine = [engine new];
 6       tires[0] = [Tire new];
 7       tires[1] = [Tire new];
 8       tires[2] = [Tire new];
 9       tires[3] = [Tire new];
10       }
11       return(self);
12 }//init
   Car的init方法创建了4个轮胎并赋值给tires数组,还创建了一台发动机赋值给engine实例变量。self = [super init],[super init]赋值给self是object-c的惯例,这么做是为了防止超类在初始化过程中返回的对象与一开始创建不一致。
  接下来,是Car类的print方法:
1 -(void)print
2 {
3   NSLog(@"%@",engine);
4   NSLog(@"%@",tires[0]);
5   NSLog(@"%@",tires[1]);
6   NSLog(@"%@",tires[2]);
7   NSLog(@"%@",tires[3]);
8 }//print
9 @end//Car

 print是通过NSlog

1 int main (int argc,const char *argv[])
2 {
3     Car *car;
4     car = [Car new];
5     [car print];
6     return(0);
7 }//main

运行程序后输出:

 

 -------------------------------------
存取方法 accessor
 
1、概念
是用来读取或者改变某个对象属性的方法
setter方法:添加一个方法去改变Car对象中的engine对象变量(如赋值);
getter方法:为代码提供了通过对象自身方位对象属性的方式;
 
下面为Car添加一些setter和getter方法,这样它就有了选用轮胎和发动机的自主权;
 1 @interface Car : NSObject
 2 {
 3     Engine *engine;
 4     Tire *tire[4];
 5 }
 6 -(Engine *)engine;
 7 -(void) setEngine: (Engine *)newEngine;
 8 -(Tire *)tireAtIndex : (int)index;
 9 -(void)setTire:(Tire *)tire atIndex:(int)index;
10 -(void)print;
11 @end//Car

   代码中的实例变量并没有变化,但是新增了2对方法,engine和setEngine:用来处理发动机的属性;tireAtIndex:和set:atIndex:用来处理轮胎的属性。

   存储方法总是成对出现的,一是用来设置属性的值,一是用来读取属性的值。有时只有一个getter方法(用于只读,例如磁盘文件大小),或者有时只有一个setter方法(例如设置密码)也是合理的。

 
2、命名规范
 setter方法的命名:加上前缀,例如:setEngine srtStringValue
 getter方法的命名:以其返回的属性名称命名,例如fillColor font ,错误的命名如getStringaValue getFont
3、设置engine属性的存取方法
1 -(Engine *)engine; 
2 -(void)setEngine : (Engine)newEngine;

  第一对存取方法用来访问发动机的属性,在代码中调用Car对象的engine方法可以访问engine方法,调用setEngine方法可以更改发动机的属性。下面是2个方法的实现:

1 -(Engine *)engine
2 {
3     return (engine);
4 }//engine:返回实例变量engine的当前值,engine返回的是指针,指向Car中的发动机对象
5 
6 -(void)setEngine:(Engine *)newEngine
7 {
8     engine = newEngine;
9 }//setEngine:将实例变量engine的赋值为参数所指向的

在代码中实际运用这些存取方法,可编写如下代码:

1 Engine *engine = [Engine new];
2 
3 [car setEngine : engine];
4 
5 NSLog(@"the car's engine is %@",[car engine]);

 

4、设置tires属性的存取方法

1 -(void)setTire:(Tire *)tire atIndex : (int)index;
2 -(Tire *)tireAtIndex:(int)index;

  由于汽车的4个轮胎都有自己不同的位置,所以Car对象包含一个轮胎的数组。这里我们 需要用索引器而不能直接访问tires数组。为汽车配置轮胎时,不仅需要知道是哪个轮胎还需要知道每个轮胎在汽车上的具体位置。

  实现代码如下:

 1 -(void)srtTire:(Tire *)tire atIndex: (int)index;
 2 {
 3     if(index < 0 || index > 3){
 4      NSLog(@"bad index (%d) in setTire:atIndex : ",index);
 5     exit(1);
 6     }
 7     tires[index] = tire;
 8 }//setTire:atIndex:
 9 
10 -(Tire *)tireAtIndex : (int)index
11 {
12     if(index < 0 ||index>3)
13     {
14       NSLog(@"bad index (%d) in tireAtIndex : ",index);
15       exit(1);
16     }
17     return(tires[index]);
18 }//tireAtIndex:

在代码中实际运用这些存取方法,可编写如下代码:

1 Tire *tire =[Tire new];
2 [car setTire : tire atIndex:2];
3 NSLog(@"tire number two is %@",[car tireAtIndex: 2]);

  因为Car类不再通过自身的方法进行部件装配,所以必须跟新main()函数来创建它们,具体调整如下:

 int main (int argc,const char *argv[])
 {
     Car *car=[Car new];
     Engine *engine = [Engine new];
     [car setEngine : engine];
     for(int i = 0 ; i < 4 ; i++)
     {
        Tire *tire = [Tire new];
        [car serTire : tire atIndex : i];
      }
     //car = [Car new];
     [car print];
     return(0);
 }//main

  这里main()创建了一辆新车,然后为其创建并配置了一台新的发动机,接下来循环4次for,每次都创建一个轮胎并安装在汽车上,然后输出汽车的详细信息并退出程序。

 
5、扩展CarParts程序
  用集成方式来创建新的发动机和轮胎,然后使用Car类的存取方法给汽车配置新的部件。
  首先创建一个新型的发动机slant6
@internet Slant6 : Engine
@end

@implementation Slant6
-(NSString *)description
{
    return(@"I am a slant-6.");
} //description
@end //Slant6

  Slant6是一种发动机,因为它们可以是Engine的子类;在car类中,setEngine:方法需要的是engine型的参数,所以我们可以传递Slant6型参数。

  Slant6中,description方法被重写,用来输出信息。

1 @interface AllWeatherRadial : Tire
2 @end
3 
4 @implementation AllWeatherRadial 
5 -(NSStirng *)description
6 {
7   return(@"I am a tire for rain or shine.");
8 } //description
9 @end //AllWeatherRadial

  最后调用main()函数,使用新型的发动机和轮胎:

 1  int main (int argc,const char *argv[])
 2  {
 3      Car *car=[Car new];
 4      //Engine *engine = [Engine new];
 5      // [car setEngine : engine];
 6      for(int i = 0 ; i < 4 ; i++)
 7      {
 8         //Tire *tire = [Tire new];
 9         [car serTire : tire atIndex : i];
10       }
11      Engine *engine = [Slant6 new];
12      [car setEngine : engine];
13      //car = [Car new];
14      [car print];
15      return(0);
16  }//main

运行结果如下:

 

 
 
posted on 2015-04-23 22:05  sally.wei  阅读(222)  评论(0编辑  收藏  举报