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 ...
随机推荐
- c++实现简单的链表
注:我是一个编程菜鸟,哪个大神看出来缺陷提点一下,感激不尽. 链表由一个个的节点串联而成,同一由first头指针管理,属于线性表中相比于数组,添加删除方便,但访问又有点慢的数据结构. 第一步:节点 N ...
- 急训 Day 1 (2)
Mushroom的区间[题目描述]Mushroom有一行数,初始时全部是0.现在Mushroom有m个区间[L,R],他希望用以下操作得到新的序列.从m个给定区间中选择一个区间[s,t],把区间中的数 ...
- Hive数据仓库
Hive 是一个基于Hadoop分布式文件系统(HDFS)之上的数据仓库架构,同时依赖于MapReduce.适用于大数据集的批处理,而不适用于低延迟快速查询. Hive将用户的HiveQL语句转换为M ...
- JQ入门学习实战演练
选择器是JQuery一大特色,所有的DOM操作.事件操作.Ajax操作都离不开选择器.熟练掌握JQuery的选择器,可以节省很多代码,很大程序上简化我们的脚本编程工作. JQuery的选择器很类似于样 ...
- B/S网站中IE6兼容问题
在HTML中定义的样式,部分样式在IE7以前的版本中的效果是不同的,所以需要在网页中定义让浏览器以IE8的模式启动. 在HEAD中定义标签meta如下: <meta http-equiv=&qu ...
- ubuntu16.04中将python3设置为默认
直接执行这两个命令即可: sudo update-alternatives --install /usr/bin/python python /usr/bin/python2 100 sudo upd ...
- plist基本操作
重要概念:某些路径下“只能读,不能写”的原因 iPhone.ipad真机上 Resouces文件夹:是只读的,无法写入. document 和temp文件夹:可读,可写. 一.工程结构
- linux学习网站分享
http://www.zhihu.com/question/19895288 http://linux.vbird.org/ 两个linux学习的网页存起来,以后学习.
- jQurey Plugin
; (function ($, window, document, undefined) { "use strict"; var defaults = { name: " ...
- 搜集资料&安装环境
开始入坑DSP,芯片是TI的TMS320C6678,硬件平台为TI官方的TMS320C6678L开发板. 官方给的资料有很多,各种用户手册.软件支持包,眼花缭乱.先尝试把开发环境搭起来,跑个Hello ...