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 ;
}
输出结果:
/*
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 =, b = , c = ;
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 ;
}
结论:
"代码无错就是优" 能获得想要的结果 挺好的嘛
问题:
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 = , num2 = , result = ;
char operate = '+';
// 根据输入的运算符选择运算方式并获得运算结果
switch (operate) {
case '+':
result = num1 + num2;
break;
case '-':
result = num1 - num2;
break;
case '*':
result = num1 * num2;
break;
case '/':
if (num2 != ) {
result = num1 / num2;
}
else{
NSLog(@"除数不能为 0"); // 是除数还是被除数啊 忘了
}
break;
}
NSLog(@"运算结果是: %.2lf", result); return ;
}
结论:
好了 变量名像点样了 也不会做没必要的判断了 还能得到想要的结果 这总没问题了吧
书摘:
"碰到问题就直觉的用计算机能够理解的逻辑来描述和表达待解决的问题和具体的求解过程,这本身没有错, 但这样的思维却使得我们的程序只为满足实现当前的需求, 程序不容易维护, 不容易扩展, 更不容易复用,从而达不到高质量代码的需求"
问题:
现在的计算器只是控制台的输出 如果要做个桌面程序 或者网站上的计算器 或者是手机应用怎么办?? 把代码拷贝过去?? 怎么让代码复用呢
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 = ;
// 选择运算方式并获得结果
switch (operate) {
case '+':
result = num1 + num2;
break;
case '-':
result = num1 - num2;
break;
case '*':
result = num1 * num2;
break;
case '/':
if (num2 != ) {
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 = , num2 = , res = ;
// 运算符
char ope = '*';
// 调用运算类的运算方法获得结果
res = [Operation getResultOfNum1:num1 andNum2:num2 withOperate:ope];
// 打印输出
NSLog(@"运算结果是:%.2lf", res); return ;
}
结论:
现在通过封装把计算和显示分开了 也就是实现了业务逻辑和界面逻辑的分离 这里主函数中的代码也精简了一些 不用去管方法里面到底是怎么得到结果的 运算类的代码也不用去动便可复用
问题:
但是 怎么让代码可以灵活的扩展呢? 比如要加一个求余 开方等运算进去, 我们现在需要改动的是在运算类的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 = ;
return result;
}
@end
加法类的声明实现:
#import "Operation.h"
// 加法运算类
@interface OperationAdd : Operation
@end #import "OperationAdd.h"
@implementation OperationAdd
// 获得两数相加结果
- (double)getResult{
double result = ;
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 = ;
result = _number1 * _number2;
return result;
}
@end
除法类的声明实现:
#import "Operation.h"
// 除法运算类
@interface OperationDiv : Operation
@end #import "OperationDiv.h"
@implementation OperationDiv
// 获得两数相除的结果
- (double)getResult{
double result = ;
if (_number2 != ) {
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 = , num2 = , res = ;
char operate = '*'; Operation * ope = [GetOperation createOperationWithOperate:operate];
ope.number1 = num1;
ope.number2 = num2; res = [ope getResult];
NSLog(@"运算结果是: %.2lf", res); return ;
}
现在如果要修改其中一种运算 对其他的运算都不会有影响了 如果想要添加一种新运算 也只需要添加一个新运算类 然后在工厂方法中修改下switch分支就行了 不需要再提供原有的运算类 所以对其他已经存在的运算类都不会有影响 这样便实现了开放-封闭原则
书摘: "编程是一门技术,更是一门艺术,不能只满足于写完代码运行结果正确就完事,时常考虑如何让代码更加简练,更加容易维护,容易扩展和复用, 只有这样才可以真正得到提高"
OC基础--多态 及 三特性小练习的更多相关文章
- OC基础 类的三大特性
OC基础 类的三大特性 OC的类和JAVA一样,都有三大特性:继承,封装,多态,那么我们就来看一下OC中类的三大特性. 1.继承 继承的特点: (1)子类从父类继承了属性和方法. (2)子类独有的属 ...
- OC基础—多态(超级简单)
前言: oc中的指针类型变量有两个:一个是编译时类型,一个是运行时类型,编译时类型由声明该变量是使用的类型决定,运行时类型由实际赋给该变量的对象决定.如果编译时类型和运行时类型不一致,就有可能出现多态 ...
- Java基础-面向对象第三大特性之多态(polymorphism)
Java基础-面向对象第三大特性之多态(polymorphism) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.多态概述 多态是继封装,继承之后,面向对象的第三大特性,多态的 ...
- 五.OC基础--1.多态,2.类对象,3.点语法,4.@property&@synthesize,5.动态类型,内省(判断对象是否遵循特定的协议,以及是否可以响应特定的消息)
五.OC基础--1.多态, 1. 多态概念,定义:多态就是某一类事物的多种形态: 表现形式: Animal *ani = [Dog new]; 多态条件:1.有继承关系 2.有方法的重写 2.多态代码 ...
- [.net 面向对象编程基础] (13) 面向对象三大特性——多态
[.net 面向对象编程基础] (13) 面向对象三大特性——多态 前面两节,我们了解了面向对象的的封装和继承特性,面向对象还有一大特性就是多态.比起前面的封装和继承,多态这个概念不是那么好理解.我们 ...
- 理解java的三大特性之多态(三)
摘自:http://cmsblogs.com/?p=52 面向对象编程有三大特性:封装.继承.多态. 封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据.对外界而已 ...
- OC基础11:基本的C语言特性2
"OC基础"这个分类的文章是我在自学Stephen G.Kochan的<Objective-C程序设计第6版>过程中的笔记. 19.定义一个int类型的指针: int ...
- OC基础10:基本的C语言特性1
"OC基础"这个分类的文章是我在自学Stephen G.Kochan的<Objective-C程序设计第6版>过程中的笔记. 1. 函数和方法的区别? (1).方法包 ...
- OC基础6:多态、动态类型和动态绑定
"OC基础"这个分类的文章是我在自学Stephen G.Kochan的<Objective-C程序设计第6版>过程中的笔记. 1.关于SEL类型的数据: (1).SEL ...
随机推荐
- linux文件系统,文件的分类
从硬盘的构造可知,每次对物理磁盘的访问的最小单位是一个盘面上的一个磁道的扇区,即使用户需要读取一个字节的数据,实际读写时都是先把该字节所在的扇区读读入到内存,然后再访问. 1.普通文件 2.目录文件 ...
- 浅谈一下关于iscroll的使用心得
因为最近的项目所有页面老板想做成苹果原生那种上下拉动有回弹效果的体验,浏览器自带是没有这种功能的,自己写的话兼容性可能会出大问题,要适配安卓的各种机型实在是难,所以我还是选择去使用移动端相对比较稳定的 ...
- 狗汪汪玩转无线电 -- GPS Hacking
狗汪汪玩转无线电 -- GPS Hacking Kevin2600 · 2015/12/09 10:12 0x00 序 GPS Hacking 在过去几年的安全会议上一直都是很受关注的议题. 但往往因 ...
- AngularJS select中ngOptions用法详解
AngularJS select中ngOptions用法详解 一.用法 ngOption针对不同类型的数据源有不同的用法,主要体现在数组和对象上. 数组: label for value in a ...
- HTML5 十大新特性(三)——视频和音频
一.视频(video) H5新加了video标签,用来播放视频,默认为一个300*150的inline-block. 二.音频(audio) H5新加了audio标签,用来播放音频,默认为一个300* ...
- asp.net用户自定义控件传参
asp.net自定义控件传参的方式有2中: ①字段的方式 在自定义控件的.ascx.cs中定义一个字段,然后在调用页面的page_load方法里面传入参数. 如 在自定义控件中设置字段 publ ...
- DOM hash
前段时间做的一个H5专题,用到了hash解决问题,特意记录一下.DOM hash的详细内容可以点击链接查看. hash就是uri中#及后面的部分,例如:www.google.com.hk#123的#1 ...
- BADI_MATERIAL_CHECK(物料主数据表的增强检查)
多次调用的BADI,'业务加载项定义的多重使用意味着可以有多个活动的 业务加载项实施.在进行调用时,将调用所有上述实施,但调用顺序不可预期. 因此,如果使用多重使用 业务加载项定义,那么顺序不得起到任 ...
- 关于nginx反向代理后获取不到客户端的真实ip地址问题
前段时间在我的网站上用nginx做了一下反向代理,最近发现不能获取客户端ip了,都是拿到的127.0.0.1的本地ip... 通过查资料后,再去看了看我的配置文件,结果发现我没有如下配置: nginx ...
- jquery中隐藏div的几种方法
//jQuery中的显示.隐藏方法 $("#id").show()://表示display:block, $("#id").hide()://表示disp ...