OC中的面向对象规范
———- android培训、java培训、期待与您交流! ———-
1引入——> 一个简单的OC程序
- 类的声明
//Person.h
#import <Foundation/Foundation.h>
@interface Person : NSObject
{
int age;
}
+ (id)person;
- (void)chat;
@end
- 类的实现
//Person.m
#import "Person.h"
@implementation Person
+ (id)person
{
return [[[self alloc] init] autorelease];
}
- (void)chat
{
NSLog(@"开启蛋b模式!");
}
@end
- 类的使用
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char* argv[])
{
@autoreleasepool{
Person *person = [Person person];
[person chat];
}
return 0;
}
Output: 开启蛋b模式!
由上面给出的示例代码我们了解到一个简单的OC类包含声明和实现两个部分。这之间的关系就好像钟表的结构一样
- @interface就好像暴露在外面的时钟表面
- @implementation就好像隐藏在时钟内部的构造实现
下面我们将进一步发掘进化我们的代码。
2继承(Inheritance)
现在我们要创建一个女孩这个类,而这个女孩有年龄和辫子长度两个属性所以可以写出下面代码:
//Girl.h
#import <Foundation/Foundation.h>
@interface Girl : NSObject
{
int age;
int lenPigtails;
}
+ (id)person;
- (void)chat;
@end
我们不难看出这跟Person类其实很像,有很多相同的部分,可总是写这些相同的东西难免让人感到不爽… …有没有一种方式能让我们写更少的代码就能体现相同的效果呢?当让答案是有的啦,这时候继承(Inheritance)闪亮登场!
继承的思想:将两个或者更多个类的公共部分抽取出来形成一个这些类的基类也就是父类从而减少代码的冗余量
继承的效果:父类的所有方法和属性子类都将拥有
回过头我们再来看一下我们所有的这两个类,不难发现有这么一个关系:没错女孩一定是个人嘛!只要有了这个前提我们就能够用继承的方式来优化我们的代码。
//Girl.h
#import "Person.h"
@interface Girl : Person
{
int lenPigtails;
}
@end
这个时候我们的代码之前的看起来整洁很多了吧。
可是这个时候你会发现一个问题,就是不管是Person对象还是Girl在main函数中产生的对象都无法访问我们的Age属性,这是为什么呢?
原来在OC中默认状况下所有成员变量的属性都是@protected(还有@public 和@private两种变量的作用域)
而protected作用域是本类和其子类可用,也就是对外隐藏不可用的。那么,我们怎么才能对其进行访问呢?
答案是让这个类提供相应的方法即可,以为在OC中所有方法默认都是public的也就全局都可访问。
这样我们就可以访问和设置人(Person类)的age属性
3封装(Encapsulation)
我们来看下最初按照这种考虑是如何提供方法的:
//Person.h
#import <Foundation/Foundation.h>
@interface Person : NSObject
{
int age;
}
+ (id)person;
- (void)chat;
- (void)setAge:(int)age;
- (int)age;
@end
//Person.m
#import "Person.h"
@implementation Person
+ (id)person
{
return [[[self alloc] init] autorelease];
}
- (void)chat
{
NSLog(@"开启蛋b模式!");
}
- (void)setAge:(int)age
{
age = age;
}
- (int)age
{
return age;
}
@end
看到上面的代码我们马上可以发现一个问题。就是在方法实现的时候我们的age好像有些乱啊~外部得到age属性的方法名和设置age属性的参数名称以及属性(成员变量)的名称好像都一样不好区分啊。
为了便于区分这里的age属性官方给了相关的建议:就是所有OC类的成员变量都要以 “_”开头
也就是说,成员变量要写成这样子:
int _age;
详细
成员变量的命名规范:
- 成员变量都以下划线 _ 开头
- 可以跟get方法的名称区分开
- 可以跟其他局部变量区分开,一看到下划线开头的变量,肯定是成员变量
get方法命名规范:
- get方法的名称一般就跟成员变量同名
set方法命名规范:
- 方法都是以set开头,而且后面跟上成员变量名,成员变量名的首字母必须大写
- 形参名称不要跟成员变量同名
这样一来相关代码变成:
- (void)setAge:(int)age
{
_age = age;
}
- (int)age
{
return _age;
}
好的这样我们提供了一套访问类内部被保护的属性的机制,这就是所谓的封装(Encapsulation).
上面看上去好像类可以正常使用了,但是现在又出现了一个问题!
当我们需要为一位哑巴创建一个类的时候我们发现了一个问题,哑巴不会说话!不能跟别人蛋b啊!可哑巴也是人类啊!怎么办呢?
4多态(Polymorphism)
有了上面的推演不难拿出解决方案:
1.将Person类继续拆分把Person类拆分成Person类和NormalPerson类
2.让chat方法从Person类中抽离出来,放到NormalPerson类中
3.让NormalPerson类继承Person类
4.以前继承Person类的类改为继承NormalPerson类,而哑巴类MuteMan责继承Person类
//Person.h
#import <Foundation/Foundation.h>
@interface Person : NSObject
{
int _age;
}
+ (id)person;
- (void)setAge:(int)age;
- (int)age;
@end
//NormalPerson.h
#import "Person.h"
@interface NormalPerson: Person
- (void)chat;
@end
//MuteMan.h
#import "Person.h"
@interface MuteMan: Person
@end
//Girl.h
#import "NormalPerson.h"
@interface Girl : NormalPerson
{
int _lenPigtails;
}
- (void)setLenPigtails:(int)lenPigtails;
- (int)lenPigtails;
@end
这样我们就将上面提出的问题解决了,这种方案的思想是什么呢?答案是:
里氏替换原则
里氏替换原则:所有基类能出现的地方,子类一样可以出现。
从上面的叙述中我们了解到在这里,Person类充当了基类,一样也可以出现的子类就是NormalPerson类。
这种处理也承载着多态的思想!
把不同的子类对象都当作父类来看,这便是多态的思想!
也就是我们在众多教学平台学到的下面这个句型的:思想上的翻译!
PS:(OC代码)
//采用方案前
Person *p1 = [Girl person]; //参看我上面Person.m中person方法的实现
//采用方案后
NormalPerson *np = [Girl person];
//将来要有男孩类可NormalPerson *np = [Boy person];