一、面向过程与面向对象
1.1 区别
(1) 面向过程(POP)和面向对象面向对象(OOP),都是指软件开发的思想。
(2) 面向过程是一种以事件为中心的编程思想,编程的时候把解决问题的步骤分析出来,然后用函数把这些步骤去实现,最后在具体步骤中按照顺序调用函数。
(3) 面向对象是一种以对象为中心的编程思想,把要解决的问题分解成各个对象,建立对象的目的不是为了完成某个步骤,而是为了描述某个对象在整个问题解决步骤中的属性和行为。
(4) 最开始的软件开发思想采用的是面向过程,但随着软件业务越来越复杂,面向过程的编程思想无法满足软件开发的需求,后来便出现了面向对象的编程思想。面向对象的方式更适合人类的解决问题的思考方式。
1.2 优缺点对比
优点 | 缺点 | |
---|---|---|
面向过程 | 解决问题的步骤清晰,开发之前就考虑好了实现方式和最终结果。 | 一个方法只能解决一个问题,代码重用性低,拓展能力差,后期维护难度和成本高。 |
面向对象 | 程序模块化和结构化,代码可继承可覆盖,易拓展和可重用,后期维护难度和成本低。 | 性能低,由于面向更高的逻辑抽象层,使得面向对象在实现的时候,不得不做出性能上面的牺牲,计算时间和空间存储大小都开销很大。 |
1.3 其他
(1) C语言是面向过程的、C++是面向对象的。
(2) 面向过程和面向对象两种编程思想地位是平等的,缺一不可。
(3) 在某些大型的游戏有时候为了提高性能,会选择面向过程的开发方式。
(4) 用面向过程的方法写出来的程序是一份蛋炒饭,而用面向对象写出来的程序是一份盖浇饭。
(5) 这两者不是对立的,而是有联系的,面向对象包含面向过程。因为对象的方法,最终是要靠面向过程来实现的。所以我个人觉得,面向过程是面向对象这棵大树末端的叶子。
二、 面向对象三大特征
面向对象的三个基本特征是:封装、继承、多态。
2.1 封装
封装就是把客观的事物封装成抽象的类,把需要提供给外界访问的变量和方法进行暴露,把不需要提供给外界访问的变量和方法进行隐藏。
这里我们可以看到,OC中也是有四种访问权限修饰符:
objective-c
@public、@protected、@private、@package
其中默认的修饰符是@private
但是这里要注意的是:OC中的方法是没有修饰符的概念的,这个和Java有很大的区别,一般都是公开访问的,即public的,但是我们怎么做到让OC中的一个方法不能被外界访问呢?
OC中是这么做的,如果想让一个方法不被外界访问的话,只需要在.m文件中实现这个方法,不要在头文件中进行定义,说白了就是:该方法有实现,没定义,这样外界在导入头文件的时候,是没有这个方法的,但是这个方法我们可以在自己的.m文件中进行使用。
封装的好处:
- 良好的封装可以减少耦合
- 类内部的实现可以自由的修改
- 类具有清晰的对外接口
2.2 继承
继承是类中的一个重要的特性,他的出现使得我们没必要别写重复的代码,可重用性很高。
继承可以使用父类所有的变量和方法,并且可以对父类的功能进行拓展。
2.3 多态
多态是指子类对父类方法的重写。
2.3.1 打印机的例子
抽象的打印机类Printer
Printer.h
objective-c
#import
@interface Printer : NSObject
- (void) print;
@end
就是一个简单的方法print
Printer.m
objective-c
#import "Printer.h"
@implementation Printer
- (void)print{
NSLog(@"打印机打印纸张");
}
@end
实现也是很简单的
下面来看一下具体的子类
ColorPrinter.h
objective-c
#import "Printer.h"
//修改父类的打印行为
@interface ColorPrinter : Printer
- (void)print;
@end
ColorPrinter.m
objective-c
#import "ColorPrinter.h"
@implementation ColorPrinter
- (void)print{
NSLog(@"彩色打印机");
}
@end
在看一下另外一个子类
BlackPrinter.h
objective-c
#import "BlackPrinter.h"
@implementation BlackPrinter
- (void)print{
NSLog(@"黑白打印机");
}
@end
这里我们在定义一个Person类,用来操作具体的打印机
Person.h
objective-c
#import "Person.h"
@implementation Person
/*
- (void) printWithColor:(ColorPrinter *)colorPrint{
[colorPrint print];
}
- (void) printWithBlack:(BlackPrinter *)blackPrint{
[blackPrint print];
}
*/
- (void) doPrint:(Printer *)printer{
[printer print];
}
@end
再来看一下测试代码:
main.m
objective-c
#import "Person.h"
#import "BlackPrinter.h"
#import "ColorPrinter.h"
int main(int argc, const charchar * argv[]) {
@autoreleasepool {
Person *person =[[Person alloc] init];
ColorPrinter *colorPrint = [[ColorPrinter alloc] init];
BlackPrinter *blackPrint = [[BlackPrinter alloc] init];
//多态的定义
/*
Printer *p1 = [[ColorPrinter alloc] init];
Printer *p2 = [[BlackPrinter alloc] init];
[person doPrint:p1];
[person doPrint:p2];
*/
//通过控制台输入的命令来控制使用哪个打印机
int cmd;
do{
scanf("%d",&cmd);
if(cmd == 1){
[person doPrint:colorPrint];
}else if(cmd == 2){
[person doPrint:blackPrint];
}
}while (1);
}
return 0;
}
下面就来详细讲解一下多态的好处
上面的例子是一个彩色打印机和黑白打印机这两种打印机,然后Person类中有一个操作打印的方法,当然这个方法是需要打印机对象的,如果不用多态机制实现的话(Person.h中注释的代码部分),就是给两种打印机单独定义个操作的方法,然后在Person.m(代码中注释的部分)中用具体的打印机对象进行操作,在main.m文件中,我们看到,当Person需要使用哪个打印机的时候,就去调用指定的方法:
objective-c
[person printWithBlack:blackPrint];//调用黑白打印机
[person printWithColor:colorPrint];//调用彩色打印机
这种设计就不好了,为什么呢?假如现在又有一种打印机,那么我们还需要在Person.h中定义一种操作这种打印机的方法,那么后续如果在添加新的打印机呢?还在添加方法吗?那么Person.h文件就会变得很臃肿。所以这时候多态就体现到好处了,使用父类类型,在Person.h中定义一个方法就可以了:
objective-c
- (void) doPrint:(Printer *)printer;
这里看到了,这个方法的参数类型就是父类的类型,这就是多态,定义类型为父类类型,实际类型为子类类型
objective-c
- (void) doPrint:(Printer *)printer{
[printer print];
}
这里调用print方法,就是传递进来的实际类型的print方法。
objective-c
Printer *p1 = [[ColorPrinter alloc] init];
Printer *p2 = [[BlackPrinter alloc] init];
[person doPrint:p1];
[person doPrint:p2];
这里的p1,p2表面上的类型是Printer,但是实际类型是子类类型,所以会调用他们自己对应的print方法。
2.3.2 iOS 多态的实际运用例子
一句话概括多态:子类重写父类的方法,父类指针指向子类。
或许你对多态的概念比较模糊,但是很可能你已经在不经意间运用了多态。比如说:
有一个tableView,它有多种cell,cell的UI差异较大,但是它们的model类型又都是一样的。
由于这几种cell都具有相同类型的model,那么你肯定会先建一个基类cell,如:
objective-c
@interface BaseCell : UITableViewCell
@property (nonatomic, strong) Model *model;
@end
然后各种cell继承自这个基类cell:
objective-c
RedCell.h
RedCell.m
GreenCell.h
GreenCell.m
BlueCell.h
BlueCell.m
objective-c
@interface RedCell : BaseCell
@end
子类cell重写BaseCell的setModel:
方法:
objective-c
// 重写父类的setModel:方法
- (void)setModel:(Model *)model {
// 调用父类的setModel:方法
super.model = model;
// do something...
}
在controller中:
objective-c
// cell复用ID array
- (NSArray *)cellReuseIdArray {
if (!_cellReuseIdArray) {
_cellReuseIdArray = @[RedCellReuseID, GreenCellReuseID, BlueCellReuseID];
}
return _cellReuseIdArray;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellResueID = nil;
cellResueID = self.cellReuseIdArray[indexPath.section];
// 父类
BaseCell *cell = [tableView dequeueReusableCellWithIdentifier:cellResueID];
// 创建不同的子类
if (!cell) {
switch (indexPath.section) {
case 0: // 红
{
// 父类指针指向子类
cell = [[RedCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellResueID];
}
break;
case 1: // 绿
{
// 父类指针指向子类
cell = [[GreenCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellResueID];
}
break;
case 2: // 蓝
{
// 父类指针指向子类
cell = [[BlueCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellResueID];
}
break;
}
}
// 这里会调用各个子类的setModel:方法
cell.model = self.dataArray[indexPath.row];
return cell;
}
不出意外,类似于上面的代码我们都写过,其实这里就运用到了类的多态性。
多态的三个条件:
- 继承:各种cell继承自BaseCell
- 重写:子类cell重写BaseCell的
setModel:
方法 - 指向:父类cell指针指向子类cell
以上,就是多态在实际开发中的体现。
合理运用类的多态性可以降低代码的耦合度让代码更易扩展。
三、常见的面向过程和面向对象的开发语言
面向过程的开发语言:
- C
- Pascal
- Fortran
- COBOL
- BASIC
面向对象的开发语言:
- Java
- C++
- C#
- Python
- Ruby
- Swift
- Objective-C
- PHP
需要注意的是,有些语言可以同时支持面向过程和面向对象的编程范式,如 C++、Python、Swift 等。这些语言可以根据开发者的需求选择使用面向过程或面向对象的编程方式。