• Objective-C 基础教程第五章,复合


    Objective-C 基础教程第五章,复合

    什么是复合?

    编程中的复合(composition)就好像音乐中的作曲(composition)一样:将多个组件组合在一起,配合使用,从而得到完整的作品。

    Car程序

    接下来我们不再用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//Carinit方法创建了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处理%@说明符时,会询问参数列表中相应的对象以得到这个对象的描述。

    技术原理上是NSLog给这个对象发送了description消息,然后对象的description方法生成了一个NSString并将其返回。在类中提供description方法就可以自定义NSLog()会如何输出对象。

    存取方法get Set

    编程人员很少会对自己编写的程序感到满意,因为软件开发是永无止境的,永远有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:新车的车主会得到一辆没有轮胎和发动机的汽车,不过装配是轻而易举的事(有时软件中的生活比现实生活要容易的多啊 😃 )。

    #import <Foundtation/Foundation.h>
    int main(int argc,const char*argv[])
    {
      //新车的车主购买一台新汽车
      Car *car = [Car new];
      
      //车主继续购买一个引擎 4个轮胎
      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);
    }
    

    扩展Car程序

    现在我们Car类已经有了存取方法,我们可以更加充分的利用它,来扩展我们的Car,比如我们可以给汽车弄个新的具体什么型号的发动机(Slant6),或者具体什么型号 牌子的轮胎(米其林)。

    @interface Slant6 :Engine//新型发动机 继承自发动机接口
    @end
      
    @implementation Slant6
    - (NSString *) description
    {
      return (@"I am a slant- 6. VROOM! VROOM!");
    }
    @end
    
    @interface MicheLin : Tire
    @end
      
    @implementation MicheLin
    - (NSString *)description
    {
      return (@"I am a tire , Extraordinary driving and driving experience(非凡驾控,持久体验)");
    }
    @end
    
    #import <Foundation/Foundation.h>
    
    int main(int argc,const char*argv[])
    {
      //新车的车主购买一台新汽车
      Car *car = [Car new];
      
      //车主继续购买一个Slant6引擎 4个米其林轮胎
      Engine *engine = [Slant6 new];
       //组装上引擎
      [car setEngine:engine];
      for(int i=0;i<4;i++)
      {
        Tire *tire = [MicheLin new];
         //组装上轮胎
        [car setTire:tire atIndex:i];
      }
      
      //开上我们更加心爱的小车车
      [car print];
    }
    
    2022-03-06 18:20:43.438697+0800 Car[13853:1512359] I am a slant- 6. VROOM! VROOM!
    2022-03-06 18:20:43.439184+0800 Car[13853:1512359] I am a tire , Extraordinary driving and driving experience(非凡驾控,持久体验)
    2022-03-06 18:20:43.439286+0800 Car[13853:1512359] I am a tire , Extraordinary driving and driving experience(非凡驾控,持久体验)
    2022-03-06 18:20:43.439333+0800 Car[13853:1512359] I am a tire , Extraordinary driving and driving experience(非凡驾控,持久体验)
    2022-03-06 18:20:43.439371+0800 Car[13853:1512359] I am a tire , Extraordinary driving and driving experience(非凡驾控,持久体验)
    

    复合还是继承

    我们的Car程序同时用到了继承和复合概念,也就是我们第一章和本章中所介绍的两个万能工具,那么什么时候用继承,什么时候用复合呢?这个问题问的不错。

    • 继承的类之间建立的关系为is a(是一个)。比如三角形是一个形状,Slant6是一个发动机,米其林是一种轮胎的名字。简单来说就是总类和一个细分类的关系。
    • 复合的类之间建立的关系为has a(有一个)。形状有一个填充颜色,汽车有一个发动机和4个轮胎。与继承不同,汽车不是一个发动机,也不是一个轮胎。简单来说就是它是由什么构成,它拥有什么,那么就可以用复合。

    新手在面向对象编程时候经常会犯这样的错误:对任何东西都想使用继承,比如让Car类继承自Engine类,这样可是不行的。我们得套用公司:is s(是一个)的关系来建立面向对象编程继承的概念,蓝猫是一个猫,狸花猫是一个猫。这种就可以用继承的方式了。复合则套用has a(有一个)的公式,猫有一个嘴巴,两只眼睛,两只耳朵,一条尾巴等等。

    实用的例子:这次假设你的程序可能会涉及有照车辆,就是需要某种执照才能合法驾驶的车辆。汽车、摩托车、牵引车都是有照车辆,套用公式is a(是一个):汽车是一个有照车辆、摩托车是一个有照车辆。所以可以用继承。创建一个类LicensedVehicle(有照车辆),再套用公式has a(有一个),有照车辆有一个牌照 (牌照所在地、牌照号码)(复合方式)。

    //牌照
    @interface Licenseplate
    {
      NSString Address;
      NSString Number;
    }
    @end
    //有照车辆
    @interface LicensedVehicle : NSObject
    {
      Licenseplate licsplate; //复合概念
    }
    @end
      
    //继承概念
    @interface Car :LicensedVehicle 
    @end
      
    @interface MotorCycle :LicensedVehicle
    @end
    

    小结

    复合是OOP的基础概念,我们通过这种技巧来创建 引用其他类的对象,在本章中用了Car来做了很多复合的例子,还介绍了OC中比较重要的存取方法,存取方法和复合是密不可分的,我们以后应该规范化用settergetter方法。

    最后还介绍了Cocoa存取方法的命名规则,不能使用get作为getter方法的名字,而是应该直接使用属性名,因为get名规则另有用途。


    __EOF__

  • 本文作者: VxerLee
  • 本文链接: https://www.cnblogs.com/VxerLee/p/15973147.html
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    ICMP协议(二)
    OpenGL-状态机 理解
    2021CCPC广州-C. Necklace(二分+贪心)
    HNUCM-2022年秋季学期《算法分析与设计》练习12
    嵌入式中C++ 编程习惯与编程要点分析
    《A Biography of the Pixel》摘阅
    nginx学习(4)Nginx 负载均衡
    Redis的介绍安装以及启动与使用、数据类型、Redis管道、Django使用Redis
    酒吧管理系统、酒吧销售系统
    搭建网站使用宝塔面板遇到的问题
  • 原文地址:https://www.cnblogs.com/VxerLee/p/15973147.html