OC基础--多态 及 三特性小练习

什么是多态

  什么是多态:

    多态就是某一类事物的多种形态    

      猫: 猫-->动物

      狗: 狗-->动物

      男人 : 男人 -->人 -->动物

      女人 : 女人 -->人 -->动物

    多态表示不同的对象可以执行相同的动作, 但是通过他们自己的实现代码来执行

  程序中的多态:父类指针指向子类对象

多态的条件

  有继承关系

  子类重写父类方法

  父类指针指向子类对象
         
狗 *g = [狗 new];
         
动物 *a = [狗
new];
          猫 *c = [猫 new];
          动物 *a = [猫 new];

  表现:当父类指针指向不同的对象的时候,通过父类指针调用被重写的方法的时候,会执行该指针所指向的那个对象的方法

多态的优点

  多态的主要好处就是简化了编程接口。它允许在类和类之间重用一些习惯性的命名,而不用为每一个新的方法命名一个新名字。这样,编程接口就是一些抽象的行为的集合,从而和实现接口的类的区分开来。

  多态也使得代码可以分散在不同的对象中而不用试图在一个方法中考虑到所有可能的对象。
这样使得您的代码扩展性和复用性更好一些。当一个新的情景出现时,您无须对现有的代码进行 改动,而只需要增加一个新的类和新的同名方法。

多态的原理

  动态绑定:

    动态类型能使程序直到执行时才确定对象的真实类型

    动态类型绑定能使程序直到执行时才确定要对那个对象调用的方法

  OC可以在运行时加入新的数据类型和新的程序模块:动态类型识别,动态绑定,动态加载

  id类型:通用对象指针类型,弱类型,编译时不进行具体类型检查

  补充:

    动态数据类型:    
在编译的时候编译器并不知道变量的真实类型,
只有在运行的时候才知道它的真实类型  并且如果通过动态数据类型定义变量, 如果访问了不属于动态数据类型的属性和方法, 编译器不会报错

    静态数据类型:    
默认情况下所有的数据类型都是静态数据类型, 在编译时就知道变量的类型, 知道变量中有哪些属性和方法 , 在编译的时候就可以访问这些属性和方法, 并且如果是通过静态数据类型定义变量, 如果访问不了属于静态数据类型的属性和方法, 那么编译器就会报错

    里氏替换原则:    子类对象能够替换其父类对象被使用。就是说“子类是父类”,比如,人是动物,但动物不一定是人,有一个Animal类和一个继承自Animal类的Person类, 
这时, Animal类型的变量可以指向Person类的实例, 但是Person类类型的变量不能指向Animal类的实例!

    开放封闭原则:软件实体(类  模块  函数等等) 应该可以扩展,但是不可修改. 这个原则其实是有两个特征:

      一个是说"对于扩展是开放的(Open for extension) " : 意味着有新的需求时,可以对已有的软件实体进行扩展,以满足新的需求

      一个是说"对于更改是封闭的(Closed for
modification)"  : 意味着软件实体一旦设计完成,就可以独立完成其工作,而不要对其进行任何修改。

多态的实现

  人喂宠物吃东西的例子:

    人(Person)  
行为: 喂宠物吃东西(feedPet)

    宠物(Pets)   行为: 吃东西(eat)

    猫(Cat)        
行为: 吃东西(eat)

    狗(Dog)       
行为: 吃东西(eat)

    (猫类和狗类 继承自宠物类)

  示例代码:

    宠物类的声明实现:

      #import <Foundation/Foundation.h>
      @interface Pets : NSObject
      // 吃东西
      - (void) eat;
      @end 

      #import "Pets.h"
      @implementation Pets
      // 吃东西
      - (void)eat{
          NSLog(@"宠物吃东西");
      }
      @end

    猫类的声明实现:

            #import "Pets.h"
            @interface Cat : Pets
            // 猫类特有的方法  抓老鼠
            - (void) catchMouse;
            @end 

            #import "Cat.h"
            @implementation Cat
            // 重写父类吃东西方法
            - (void)eat{
                NSLog(@"人喂宠物猫吃东西");
            }
        // 实现猫类特有的方法 抓老鼠
        - (void)catchMouse{
           NSLog(@"老猫抓老鼠");
       }
      @end

    狗类的声明实现:

      #import "Pets.h"
      @interface Dog : Pets
      @end 

      #import "Dog.h"
      @implementation Dog
      // 重写父类吃东西方法
      - (void)eat{
          NSLog(@"人喂宠物狗吃东西");
      }
      @end

    人类的声明实现:      

      #import "Pets.h"
      @interface Person : NSObject
      // 喂宠物吃东西
      + (void) feedPet:(Pets *) pet;
      @end

      #import "Person.h"
      @implementation Person
      // 喂宠物吃东西
      + (void)feedPet:(Pets *)pet{
          [pet eat];
      }
      @end

    Main.m :

      #import <Foundation/Foundation.h>
      #import "Person.h"
      #import "Pets.h"
      #import "Dog.h"
      #import "Cat.h" 

      int main(int argc, const char * argv[]) {
          // 多态的体现
          Pets * pet = [Pets new];
          [Person feedPet:pet];    

          pet = [Dog new];
          [Person feedPet:pet];
      
          pet = [Cat new];
          [Person feedPet:pet];    

          return 0;
      }

    输出结果:      

       /*
            2015-08-31 18:10:06.659 多态示例[833:53348] 宠物吃东西
        2015-08-31 18:10:06.660 多态示例[833:53348] 人喂宠物狗吃东西
        2015-08-31 18:10:06.660 多态示例[833:53348] 人喂宠物猫吃东西           */

  》在上面代码中我们将Dog 和 Cat的实例赋值给了Pets类型的变量 pet  , 即父类类型的指针指向了子类的对象, 这便是里氏替换原则的体现

  》我们给Person类定义了一个类方法 :   + (void) feedPet:(Pets) pet;  这个方法是接收一个Pets类型的对象作为参数, 然后再方法体里通过传进来的对象调用吃的方法(eat), 我们给feedPet方法传递的参数都是Pets类型的变量 pet,  但是通过输出结果可以知道, 实际上是分别调用了宠物   狗 和 猫  的吃的方法  也就是说:当父类指针指向不同的对象的时候,通过父类指针调用被重写的方法,会执行该指针所指向的那个对象的方法, 这就是多态

多态注意点

   
父类指针指向子类对象, 如果需要调用子类特有的方法, 必须先强制类型转换为子类才能调用

    Pets * pet = [Cat new];
    // 错误信息: No visible @interface for ‘Pets‘ declares the selector ‘catchMouse‘
    [pet catchMouse];
    // 类型转换后在调用方法
    [(Cat *)pet catchMouse]; 

封装继承多态练习(示例源于 大话设计模式  程杰著)

实现一个计算机控制台程序   要求输入两个数和操作符   输出结果

  1.0版:

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    double a =1, b = 10, c = 0;
    char d = ‘+‘;
    if(d == ‘+‘){
        c = a + b;
    }
    if( d == ‘-‘){
        c = a - b;
    }
    if( d == ‘*‘){
        c = a * b;
    }
    if( d == ‘/‘){
        c = a / b;
    }
    NSLog(@" 运算结果:%.2lf", c);
    return 0;
}

  结论:

    "代码无错就是优" 能获得想要的结果  挺好的嘛

  问题:

   1: 变量命名不规范   a b c d  没啥意义  看着名字也不知道是用来干嘛的

   2: 注释都没有  程序只有自己暂时能看懂

   3: 每个if都要判断一遍

    4: 做除法时如果除以 0 会怎么样

  2.0版:

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    // 用于计算的两个数字和运算符应有用户输入  此处写死在程序中
    double num1 = 10, num2 = 20, result = 0;
    char operate = ‘+‘;
    // 根据输入的运算符选择运算方式并获得运算结果
    switch (operate) {
        case ‘+‘:
            result = num1 + num2;
            break;
        case ‘-‘:
            result = num1 - num2;
            break;
        case ‘*‘:
            result = num1 * num2;
            break;
        case ‘/‘:
            if (num2 != 0) {
                result = num1 / num2;
            }
            else{
                NSLog(@"除数不能为 0");  // 是除数还是被除数啊  忘了
            }
            break;
    }
    NSLog(@"运算结果是: %.2lf", result);    

    return 0;
}

  结论:

    好了  变量名像点样了  也不会做没必要的判断了  还能得到想要的结果  这总没问题了吧

  书摘:

    "碰到问题就直觉的用计算机能够理解的逻辑来描述和表达待解决的问题和具体的求解过程,这本身没有错, 但这样的思维却使得我们的程序只为满足实现当前的需求, 程序不容易维护, 不容易扩展, 更不容易复用,从而达不到高质量代码的需求"

  问题:

    现在的计算器只是控制台的输出  如果要做个桌面程序  或者网站上的计算器  或者是手机应用怎么办??  把代码拷贝过去?? 怎么让代码复用呢

  3.0版:

  Operation类声明文件:

// 添加一个Operation运算类
#import <Foundation/Foundation.h>

@interface Operation : NSObject
// 传入两个数字 和 一个操作符 获得运算结果
+ (double) getResultOfNum1:(double) num1 andNum2: (double) num2 withOperate:(char) operate;
@end

  Operation类实现文件:

#import "Operation.h"

@implementation Operation
// 根据传入的两个数字和运算符获得运算结果
+ (double)getResultOfNum1:(double)num1 andNum2:(double)num2 withOperate:(char)operate{
    // 用于存储结果
    double result = 0;
    // 选择运算方式并获得结果
    switch (operate) {
        case ‘+‘:
            result = num1 + num2;
            break;
        case ‘-‘:
            result = num1 - num2;
            break;
        case ‘*‘:
            result = num1 * num2;
            break;
        case ‘/‘:
            if (num2 != 0) {
                result = num1 / num2;
            }
            else{
                NSLog(@"除数不能为 0");
            }
            break;
    }
    // 返回运算结果
    return result;
}
@end

  Main.m:

#import <Foundation/Foundation.h>
#import "Operation.h" 

int main(int argc, const char * argv[]) {
    // 两个参与运算的数字  res用于存储运算结果
    double num1 = 10, num2 = 20, res = 0;
    // 运算符
    char ope = ‘*‘;
    // 调用运算类的运算方法获得结果
    res = [Operation getResultOfNum1:num1 andNum2:num2 withOperate:ope];
    // 打印输出
    NSLog(@"运算结果是:%.2lf", res);    

    return 0;
}

  结论:

    现在通过封装把计算和显示分开了  也就是实现了业务逻辑和界面逻辑的分离  这里主函数中的代码也精简了一些 不用去管方法里面到底是怎么得到结果的  运算类的代码也不用去动便可复用

  问题:

    但是 怎么让代码可以灵活的扩展呢? 比如要加一个求余 开方等运算进去,  我们现在需要改动的是在运算类的switch中添加分支就行了, 但是这样真的好吗? 只是加一个运算符却需要其他已经有的运算符都来参与编译

  书上举例:

    "现在公司要你维护薪资管理系统, 原本只有技术人员(月薪) 销售(底薪+提成) 经理(年薪+股份)这三种运算算法, 现在需要加一个兼职员工(日薪)的算法, 按照现在这种做法, 公司必须把包含原有三种算法的类交给你来修改, 如果一不小心将兼职员工工资算法加进去的同时,顺手把技术人员的月薪提高那么一丢丢, 是不是很爽呢, 总有人不爽的"

  优化:

    将加减乘除等运算做分离, 修改其中一个也不影响其他, 增加运算算法也不影响已有的算法, 这就是对扩展开放, 对修改封闭--开放-封闭原则

  4.0版:

  Operation类的声明实现:    

#import <Foundation/Foundation.h>
 @interface Operation : NSObject
{
    @public
    double _number1;
    double _number2;
}
- (void) setNumber1: (double) number1;
- (void) setNumber2: (double) number2;
// 获取运算结果
- (double) getResult;
@end 

#import "Operation.h"
@implementation Operation
-  (void)setNumber1:(double)number1{
    _number1 = number1;
}
- (void)setNumber2:(double)number2{
    _number2 = number2;
}
// 获取运算结果
- (double)getResult{
    double result = 0;
    return result;
}
@end

  加法类的声明实现:

#import "Operation.h"
// 加法运算类
@interface OperationAdd : Operation
@end

#import "OperationAdd.h"
@implementation OperationAdd
// 获得两数相加结果
- (double)getResult{
    double result = 0;
    result = _number1 + _number2;
    return result;
}
@end

  减法类的声明实现:

#import "Operation.h"
// 减法运算类
@interface OperationSub : Operation
@end 

#import "OperationSub.h"
@implementation OperationSub
// 获得两数相减的结果
- (double)getResult{
    double result;
    result = _number1 - _number2;
    return result;
}
@end

  乘法类的声明实现:

#import "Operation.h"
// 乘法运算类
@interface OperationMul : Operation
@end 

#import "OperationMul.h"
@implementation OperationMul
// 获得两数相乘的结果
- (double)getResult{
    double result = 0;
    result = _number1 * _number2;
    return result;
}
@end

  除法类的声明实现:

#import "Operation.h"
// 除法运算类
@interface OperationDiv : Operation
@end 

#import "OperationDiv.h"
@implementation OperationDiv
// 获得两数相除的结果
- (double)getResult{
    double result = 0;
    if (_number2 != 0) {
        result = _number1 / _number2;
    }
    else{
        NSLog(@"除数不能为 0");
    }
    return result;
}
@end

  工厂类的声明实现:

#import "OperationAdd.h"
#import "OperationSub.h"
#import "OperationMul.h"
#import "OperationDiv.h" 

// 专门用于获得运算类实例
@interface GetOperation : NSObject
+ (Operation *) createOperationWithOperate: (char) operate;
@end 

#import "GetOperation.h"
@implementation GetOperation
// 获得运算类实例
+ (Operation *)createOperationWithOperate:(char)operate{
    // 多态的应用  父类指针指向子类实例对象
    Operation * ope = nil;
    // 根据传入的操作符获得相应的实例对象
    switch (operate) {
        case ‘+‘:
            ope = [OperationAdd new];
            break;
        case ‘-‘:
            ope = [OperationSub new];
            break;
        case ‘*‘:
            ope = [OperationMul new];
            break;
        case ‘/‘:
            ope = [OperationDiv new];
            break;
    }
    return ope;
}
@end

  Main.m:

#import <Foundation/Foundation.h>
#import "GetOperation.h" 

int main(int argc, const char * argv[]) {
    double num1 = 10, num2 = 20, res = 0;
    char operate = ‘*‘;

    Operation * ope = [GetOperation createOperationWithOperate:operate];
    ope.number1 = num1;
    ope.number2 = num2;    

    res = [ope getResult];
    NSLog(@"运算结果是: %.2lf", res);    

    return 0;
}

   现在如果要修改其中一种运算 对其他的运算都不会有影响了  如果想要添加一种新运算  也只需要添加一个新运算类  然后在工厂方法中修改下switch分支就行了  不需要再提供原有的运算类  所以对其他已经存在的运算类都不会有影响   这样便实现了开放-封闭原则

书摘: "编程是一门技术,更是一门艺术,不能只满足于写完代码运行结果正确就完事,时常考虑如何让代码更加简练,更加容易维护,容易扩展和复用, 只有这样才可以真正得到提高"

时间: 2024-10-05 21:49:09

OC基础--多态 及 三特性小练习的相关文章

OC基础 类的三大特性

OC基础  类的三大特性 OC的类和JAVA一样,都有三大特性:继承,封装,多态,那么我们就来看一下OC中类的三大特性. 1.继承 继承的特点: (1)子类从父类继承了属性和方法. (2)子类独有的属性和方法不能被父类使用. (3)子类独有的属性和方法,不能够在子类之间互相使用. (4)子类能够重写父类的方法,子类调用该方法时:首先调用子类的方法,如果子类没有该方法,就调用父类的该方法. (5)子类对象指针可以转换成父类对象指针,但是父类对象指针不能够转换成子类对象指针. 三种权限变量的继承:

OC基础—多态(超级简单)

前言: oc中的指针类型变量有两个:一个是编译时类型,一个是运行时类型,编译时类型由声明该变量是使用的类型决定,运行时类型由实际赋给该变量的对象决定.如果编译时类型和运行时类型不一致,就有可能出现多态. 正文: 新建一个CHPerson类 1 @interface CHPerson : NSObject 2 -(void)eat; 3 -(void)play; 4 @end 5 --------------------------------------------------------- 6

oc基础知识回顾三

0.概念 1>协议(Protoco)是一系列方法的列表,其声明的方法可以被任何类实现,一般称为代理模式. 2>在.h文件中@protocol声明存在协议,跟@class一样 1.作用: 1>可以声明很多方法,但是不能声明成员变量. 2>如果某个类遵守了某个协议,则这个类拥有协议中的所有方法声明. 3>如果父类遵守了某个协议,则子类也遵守了这个协议, 2.基本语法 1>定义协议 // 定义一个协议,协议名称是myprotocol,NSObject是基协议,每个协议必须遵守

c#基础多态的三种手段

多态的概念概念:让一个对象能够表现出多种的状态(类型) 实现多态的3种手段:1.虚方法 2.抽象类 3.接口 //1.虚方法 //1).虚方法//步骤://1.将父类的方法标记为虚方法 ,使用关键字 virtual,这个函数可以被子类重新写一个遍. public class Employee { public virtual void DaKa() { Console.WriteLine("九点打卡"); } } public class Manager : Employee { pu

理解java的三大特性之多态(三)

摘自:http://cmsblogs.com/?p=52 面向对象编程有三大特性:封装.继承.多态. 封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据.对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方法. 继承是为了重用父类代码.两个类若存在IS-A的关系就可以使用继承.,同时继承也为实现多态做了铺垫.那么什么是多态呢?多态的实现机制又是什么?请看我一一为你揭开: 所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编

iOS开发OC基础:OC基础概念总结,OC面向对象的思想

一.什么是OOP: OOP(Object Oriented Programming):面向对象编程 二.面向对象和面向过程的区别: 面向过程编程:分析解决问题的步骤,实现函数,依次使用面向对象编程:分解问题组成的对象,协调对象间的联系和通信,解决问题. 面向过程是以事件为中心,关心的是完成这个事件的详细步骤:面向对象是以事物为中心,关心的是事物应该具备的功能,而完成一个事件只是事物所有功能里面的一个小功能(以过程为中心,以对象为中心) 三.类和对象 对象定义了解决问题的步骤中的行为,不刻意完成一

oc基础知识(转)

1.项目经验 2.基础问题 3.指南认识 4.解决思路 ios开发三大块: 1.Oc基础 2.CocoaTouch框架 3.Xcode使用 -------------------- CocoaTouch Media Core Services Core OS -------------------- System Framework OC的类声明,定义域 OC关键字定义为  @class O-C特有的语句for(..in ..)迭代循环,其他的条件和循环语句和c一样 OC对面向对象的概述 (1)

OC基础01

搜索 "==>" 寻找标记 OC基础总结: Objective-C具有相当多的动态特征,如: " 动态类型 , 动态绑定 , 动态加载" ==> copy mutableCopy 浅复制: 在复制操作中,对于被复制的对象的每一层复制都是指针复制. 深复制: 在复制操作中,对于被复制的对象至少有一层是对象复制 完全复制: 在复制操作中,对于被复制的对象的每一层都是对象复制. 一般来讲: 浅复制复制引用对象的指针 深复制 复制引用对象的内容 retain &

Java基础知识的三十个经典问答

Java基础知识的三十个经典问答 1.面向对象的特点 抽象: 抽象是或略一个主题中与当前目标的无关的因素,一边充分考虑有关的内容.抽象并不能解决目标中所有的问题,只能选择其中的一部分,忽略其他的部分.抽象包含两个方面:一是过程抽象:一是数据抽象. 继承 继承是一种联接类的层次模型,允许和鼓励类的重用,提供了一种明确的共性的方法.对象的一个新类可以从现有的类中派生,这叫做类的继承.心累继承了原始类 的特性,新类称为原始类的派生类或者是子类,原始类称为新类的基类或者父类.子类可以从父类那里继承父类的