封裝、繼承和多態
前言
我們常說,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