Objective-C复合(Compostion)——存取方法

0x01 存取方法是间接的一个例子

上一篇中,我们已经构造了一台“车”。

假设现在我想要随时更换自己喜欢的发动机和轮胎,就要用到“存取方法”。

“存取方法”是用来读取或改变某个对象属性的方法,例如此前见过的setFillColor:就是一个存取方法。

如果我们要对其他对象中的属性进行操作,应该尽量使用对象提供的存取方法,绝对不要直接改变对象里面的值,而应该使用setter方法进行更改。

正确使用“存取方法”将会使程序更加灵活。


0x02 方法的命名规则

1、虽然说方法的命名是自由的,但为了程序的可读性,我们还是应该遵循Cocoa的惯例:

setter方法应根据它所要更改的属性名称来命名,如“set+属性名”;

getter方法只需以其返回的属性名命名即可,即“属性名”。

因为get这个词在Cocoa中有特殊含义,如果get出现在Cocoa的方法名称中,就意味着这个方法会将传递给它的参数作为指针来返回数值。


2、setter方法和getter方法一般是成对出现的,当然也可以不成对出现。如对于只读特性只有getter方法,对于密码特性只有setter方法。


0x03 存取方法的实现


我们先在Car的接口中对添加的setter和getter方法进行声明:

@interface Car : NSObject
{
  Engine *engine;
  Tire *tires[4];
}

//New Code
- (Engine *) engine;
- (void) setEngine: (Engine *) newEngine;
- (Tire *) tireAtIndex: (int) index;
- (void) setTire: (Tire *) tire atIndex: (int) index;
//New Code

- (void) print;
@end // Car


第一对存取方法是为了访问发动机的属性:

- (Engine *) engine;
- (void) setEngine: (Engine *) newEngine;

调用Car对象的engine方法可以访问engine变量,调用setEngine:方法可以更改发动机属性。

下面是这两个方法的实现:

- (Engine *) engine
{
  return (engine);
} // engine
- (void) setEngine: (Engine *) newEngine
{
  engine = newEngine;
} // setEngine

我们看到getter方法engine返回的是一个指针,指向Car中的Engine对象。实际上是返回实例变量engine的当前值;

同样的,setter方法setEngine:将newEngine的值赋给engine实例变量。实际上被修改的是engine的指针值。

在Objective-C中所有对象间的交互都是通过指针实现的!


第二对存取方法是为了访问轮胎的属性:

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

由于汽车的4个轮胎处于4个位置,所以Car对象中必须包含一个表示轮胎位置的数组:

- (void) setTire: (Tire *) tire atIndex: (int) index
  {
  if (index < 0 || index > 3) {
   NSLog (@"bad index (%d) in setTire:atIndex:",index);
   exit (1);
}
  tires[index] = tire;
} // setTire:atIndex:
- (Tire *) tireAtIndex: (int) index
{
  if (index < 0 || index > 3) {
   NSLog (@"bad index (%d) in "tireAtIndex:", index);
   exit (1);
}
  return (tires[index]);
} // tireAtIndex:

因为tire是一个C风格的数组,对于其索引值是否有效,编译器是可能直接忽略的。所以我们自己得用if条件语句来判断index值是否合理,这是一种防御式编程思想

setTire:方法的声明格式看起来不太一样,这是因为它拥有两个参数,往后学习会更加理解这种写法。


最后修改main()函数:

int main (int argc, const char * argv[]) {
  Car *car = [Car new];
  //事实上可以在Car类中把init方法删掉,因为下面的代码已经做了init方法的工作

  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];
  return (0);
} // main

程序运行结果没有变化,但我们改进了它的内部结构,使程序更加灵活:

I am an engine. Vrooom!
I am a tire. I last a while.
I am a tire. I last a while.
I am a tire. I last a while.
I am a tire. I last a while.

0x04 存取方法的应用

我们已经为Car类添加了存取方法,那换配置就是随心所欲的事了。

现在来为这台车换一个发动机:

@interface Slant6 : Engine
@end // Slant6

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

创建这台Slant6发动机,继承了Engine超类的属性。但是重写了description方法,输出了新发动机的信息。


接着我们换轮胎:

@interface AllWeatherRadial : Tire
@end // AllWeatherRadial

@implementation AllWeatherRadial
- (NSString *) description
{
  return (@"I am a tire for rain or shine.");
} // description
@end // AllWeatherRadial


最后修改main()函数,这台车使用了全新的发动机和轮胎:

int main (int argc, const char * argv[]) {
  Car *car = [Car new];
  for (int i = 0; i < 4; i++) 
{

   //New Code

   Tire *tire = [AllWeatherRadial new];
   //New Code
   [car setTire: tire atIndex: i];
  }

  //New Code
  Engine *engine = [Slant6 new];
  //New Code

  [car setEngine: engine];
  [car print];
  return (0);
} // main

运行程序后,可以发现输出已经更新为新配置了:

I am a slant- 6. VROOOM!
I am a tire for rain or shine.
I am a tire for rain or shine.
I am a tire for rain or shine.
I am a tire for rain or shine.


猜你喜欢

转载自blog.csdn.net/qq_33737036/article/details/81024914