封装、继承和多态
前言
我们常说,OC语言中的三大特征是封装、继承和多态,这三大特点本质来说就是可以让我们的代码“活”起来,不再是牵一发动全身类型的代码,让我们开发者可以在改变、增添功能时,不再需要去基类去调整我们的代码,只需要书写好新的子类即可。
封装
封装的定义:
隐藏内部实现,稳定外部接口
好处:
使用起来更加简单
变量更加安全
可以隐藏内部实现
开发速度更加快捷
作用:
类来封装了属性和方法
方法封装了实现的代码
使用类来封装成员变量
NSString *_name;
NSInteger age;
NSString *_homeAddress;
使用property来封装成员变量,实现变量安全
@property(nonatomic,strong)NSString *name;
@property (nonatomic,assign)NSInteger age;
@property (nonatomic,strong)NSString *homeAddress;
使用类来封装功能代码
-(void)hellWorld;
例子:
main.m文件中:
NSString *name=@"Jack";
NSInteger age= 25;
NSString *homeAddress =@"G2";
NSLog(@"Student 's name is %@,%ld years old,living in %@",name,age,homeAddress);
Student *stu=[[Student alloc]init];
[stu hellWorld];
// [stu hiGuys];
Student.h文件:
#import <Foundation/Foundation.h>
//在OC中,几乎所有的类都继承于NSObject
@interface Student : NSObject
{
//使用类来封装成员变量
// NSString *_name;
// NSInteger age;
// NSString *_homeAddress;
}
//使用property来封装成员变量,实现变量安全
@property(nonatomic,strong)NSString *name;
@property (nonatomic,assign)NSInteger age;
@property (nonatomic,strong)NSString *homeAddress;
//使用类来封装功能代码
-(void)hellWorld;
@end
Student.m文件:
//重写init方法
-(id)init{
if(self=[super init]){
_name=@”Jack”;
_age=25;
_homeAddress=@”G2”;
}
return self;
}
-(void)hellWorld{
NSLog(@”helloWorld”);
//打印哪个类里面的哪个方法
NSLog(@”%s”,func);
NSLog(@”%s”,FUNCTION);
[self hiGuys];
}
//私有方法:@interface中没有相关声明的方法,可以把他们看作私有方法,仅在类的实现文件中使用
-(void)hiGuys{
NSLog(@”我是私有方法”);
NSLog(@”%s”,FUNCTION);
}
继承
继承的作用:
继承是避免冗余,提高代码的可重用性和可维护性的有效手段
继承的传递性:
直接父类 间接父类
继承需要符合的关系:is-a
子类与父类的关系:
子类和父类都需要满足 is-a关系,才存在继承
继承概念下的is-a 关系是个单向的关系
子类具有父类的属性和行为,以及自身特殊的属性和行为
例子:
首先我们如下图:建立以下的类:
把这些类的父类设置为:Father
我们对父类开始编写一些成员变量和方法:
.h文件:
@interface Father : NSObject
@property (nonatomic,strong)NSString *name;
@property (nonatomic,assign)NSInteger age;
@property (nonatomic,strong)NSString *hobby;
-(void)sayHello;
-(void)charge;
@end
.m文件:
@implementation Father
-(void)sayHello{
NSLog(@"Hello everybody,my name is %@",self.name);
}
-(void)charge{
NSLog(@"老子有钱,你随便刷");
}
@end
然后我们开始对子类开始编写:
Son:
.h文件:
@interface Son : Father
@property (nonatomic,strong)NSString *homeAddress;
-(void)race;
@end
.m文件:
@implementation Son
//重写父类中的charge的方法
-(void)charge{
[super charge];
NSLog(@"拿老爸的卡出来刷一下");
}
-(void)sayHello{
NSLog(@"home address is %@",self.homeAddress);
}
-(void)race{
NSLog(@"race");
}
@end
SonA .m文件:
@implementation SonA
-(void)sayHello{
[super sayHello];
NSLog(@"I am SonA");
}
@end
SonB .m文件:
@implementation SonB
-(void)sayHello{
[super sayHello];
NSLog(@"I am SonB");
}
@end
最后在主函数进行测试:
Father *father=[[Father alloc]init];
father.name=@"Jack";
[father sayHello];
[father charge];
NotInheritedSon *aSon=[[NotInheritedSon alloc]init];
aSon.name=@"xxx";
[aSon sayHello];
[aSon charge];
Son *son =[[Son alloc]init];
[son charge];
son.homeAddress=@"GZ";
[son sayHello];
[son race];
多态
大家会注意到上面的代码里面还有SonA和SonB没用在主函数吧?这就是我们接下来讲的多态的一个例子。
多态的定义:
多态就是对于不同对象相应同一个方法时做出的不同反应,它是建立在继承的基础上面。
特点:
1.继承与同一个父类的子类,他们本身具有自己的特征
2.继承与同一个父类的子类,在执行同一命令的时候,可以具有不同的效果
SonA *sonA=[[SonA alloc]init];
sonA.name=@"sonA";
[sonA sayHello];
SonB *sonB=[[SonB alloc]init];
sonB.name=@"sonB";
[sonB sayHello];
这就是输出的结果,我们可以看到在子类通过重写父类的方法,可以达到不同子类调用同一个父类的方法可以数出不同的结果,
当然如果要重写父类的时候,还有带上父类的效果的话,就要使用 [super 父类中的的方法名]来让子类重写的时候保持原来的父类方法的输出。
多态的好处:
1.可以简化编程接口
允许多个类中定义同一消息借口
可以定义一个通用的调用方法,以简化调用
2.把不同的子类对象都当做父类来看
可以屏蔽不同子类对象之间的差异,写出通用的代码
做出通用的编程,以适应需求的不断变化。
例子:
按照下图建立以下类:(让Dog Cat类继承于Animal类,其他都继承于NSObject)
主函数如下:
id animal=nil;
//由于id类型的通用性质,我们可以将创建好的任意对象赋值给animal
animal=[[Cat alloc]init];
[animal eat];
animal=[[Dog alloc]init];
[animal eat];
Animal *animalB=nil;
animalB =[Cat new];
[animalB eat];
animalB =[Dog new];
[animalB eat];
Animal类如下:
@interface Animal : NSObject
-(void)eat;
@end
@implementation Animal
-(void)eat{
NSLog(@"动物吃东西");
}
@end
Cat类如下:
@implementation Cat
-(void)eat{
[super eat];
NSLog(@"Cat eats fish");
}
@end
Dog类如下:
@implementation Dog
-(void)eat{
[super eat];
NSLog(@"Dog eats bone");
}
@end
开闭原则和里氏替换原则
开闭原则:
对扩展开放,对修改关闭
里氏替换原则:
任何基类可以出现的地方,子类一定可以出现,
如果我们增加一个person类,由于写人喂养XXX动物,这样是不是比如我们喂养上述的动物,就要写[p feedCat:cat]和 [p feedDog:dog]这两个方法,那我们如果还有增加十几只动物呢?那样我们写代码就会有很大的冗余度;所以我们要遵循开闭原则和里氏替换原则
主函数main.m:
Cat *cat=[Cat new];
Dog *dog=[Dog new];
Person *p=[[Person alloc]init];
//[p feedCat:cat];
// [p feedDog:dog];
[p feedAnimal:cat];
Person *p=[[Person alloc]init];
[p feedAnimal:cat];
Person.h文件:
#import <Foundation/Foundation.h>
#import "Cat.h"
#import "Dog.h"
#import "Animal.h"
@interface Person : NSObject
-(void)feedCat:(Cat* )cat;
-(void)feedDog:(Dog* )dog;
-(void)feedAnimal:(Animal * )animal;
@end
Person.m文件:
@implementation Person
-(void)feedCat:(Cat* )cat{
NSLog(@"人喂猫");
[cat eat];
}
-(void)feedDog:(Dog* )dog{
NSLog(@"人喂狗");
[dog eat];
}
-(void)feedAnimal:(Animal * )animal{
if([animal isMemberOfClass:[Cat class]]){
NSLog(@"人喂猫");
[animal eat];
}
if([animal isMemberOfClass:[Dog class]]){
NSLog(@"人喂狗");
[animal eat];
}
}
@end