什么是多态算法
什么是多态:编程
多态就是某一类事物的多种形态 设计模式
猫: 猫-->动物函数
狗: 狗-->动物优化
男人 : 男人 -->人 -->动物网站
女人 : 女人 -->人 -->动物spa
多态表示不一样的对象能够执行相同的动做, 可是经过他们本身的实现代码来执行设计
程序中的多态:父类指针指向子类对象 指针
多态的条件code
有继承关系
子类重写父类方法
父类指针指向子类对象
狗 *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分支就好了 不须要再提供原有的运算类 因此对其余已经存在的运算类都不会有影响 这样便实现了开放-封闭原则
书摘: "编程是一门技术,更是一门艺术,不能只知足于写完代码运行结果正确就完事,时常考虑如何让代码更加简练,更加容易维护,容易扩展和复用, 只有这样才能够真正获得提升"