前言
曾经做java开发时,接触到很多编程思想、设计模式,后来转向iOS后,常常会思考如何将这些知识融入到iOS开发当中。今天想分享的就是在java当中无处不在,但是在OC中却很少被提及到的概念——面向接口编程。
什么是接口
接口的特征为:只声明方法,不实现方法。简单来说,接口是某些行为的抽象表达,也是一组协议或约定,我们不关心某个具体类,而只关心这个类是否遵守并实现了这些约定。例如飞机和鸟,都有共同的一种行为:飞。因为飞机和鸟并没有相同的父类可以继承(关于抽象类和接口区别以及使用场景,有兴趣的小伙伴可以google一下,很多文章分析的很透彻了,这里我就不在赘述。),因此我们就可以抽象出一个接口类IFly,与java不同的是,iOS使用protocol关键字来定义接口。
@protocol IFly <NSObject>
- (void)fly;
@end
飞机的实现类
Plane.h
@interface Plane : NSObject <IFly>
@end
Plane.m
@implementation Plane
- (void)fly
{
NSLog(@"飞机飞");
}
@end
鸟的实现类
Bird.h
@interface Bird : NSObject <IFly>
@end
Bird.m
@implementation Bird
- (void)fly
{
NSLog(@"小鸟飞");
}
@end
使用方法:
id<IFly> flyBehaviour = [Plane new];
[flyBehaviour fly];
flyBehaviour = [Bird new];
[flyBehaviour fly];
控制台打印:
IOPDemo[20101:1939006] 飞机飞
IOPDemo[20101:1939006] 小鸟飞
只要将flyBehaviour指向不同的实现了IFly接口的对象,就能体现出不同的具体行为,这也是多态的表现形式。
面向接口编程的好处
设计模式原则中有一点:依赖倒置原则。即
- 高层模块不应该依赖低层模块,两者都应该依赖其抽象;
- 抽象不应该依赖细节;
- 细节应该依赖抽象;
面向接口编程可以很好地遵守这个设计模式原则。以上例子中,抽象就是指IFly类,细节就是指Plane或Bird这样的具体类。这样做的好处是可以降低类与类之间的耦合,易于程序扩展,可以在不破坏上层代码的情况下修改甚至替换整个底层代码。面向接口编程还有其他好处:
- 可以在代码运行期间,动态地修改类的行为。在不同的条件下,由高层模块决定使用哪个具体类。
- 并行开发。在定义接口之后,业务开发人员只需调用接口方法而不用关心这些方法是否已经实现,而底层开发人员则可以根据需求逐步完善具体类的代码。
- 便于单元测试。封装性越好的代码,越容易测试。面向接口编程可以很好的将业务代码模块化,从而针对不同的逻辑进行测试。
- 有更多的好处,希望大家可以在使用过程当中去感受。
再举个简单的例子。当移动端和服务端协商了网络请求以及返回值的参数,即使服务端的接口并没有完成,也不影响移动端的开发人员进行开发,同时移动端开发人员也并不关心后台逻辑如何变化,只需保证入参及出参的正确性即可。
iOS编程中使用接口
首先看一下项目大致结构:
结构图.png
service是ViewController中的一个接口属性,InterfaceService是实现了InterfaceProtocol协议的实现类。在页面调用service的方法时,实际是调用了InterfaceService这个实现类的方法,在实现类中可以使用很多第三方的网络请求框架,如HYB,YTK等等。这些第三方库是基于AF进行了二次封装,优点是可以将请求的逻辑统一,减少上层的代码量,甚至替换掉底层AF,更换其他框架。最后AF请求服务器,并将数据一层一层向上传递直至ViewController。
那么相比于直接在ViewController中调用HYB或者YTK的网络请求,中间多的一层InterfaceService优点是什么呢?
在我们公司目前有很多业务模块如任务模块,商城模块等等。不同的业务模块有不同的服务器地址,不同的服务器请求参数形式也都不一样。如果在VC层中直接调用第三方网络请求,那么就会造成很多的重复判断代码。因此增加的InterfaceService又称为业务请求层,在这一层的代码中,我可以针对不同的服务器统一管理域名,请求配置或请求方式,编写VC的业务开发人员只需传递业务所需要的关键参数如id,分页参数等等,同时通过回调拿到网络请求的返回值,而不用关心底层如何实现,并行开发提高开发效率,也利于编写业务请求层的开发人员进行单元测试。
下面是主要类的截图
项目目录结构.png
接口文件 InterfaceProtocol.h
@protocol InterfaceProtocol <NSObject>
@optional
/**
获取首页数据
@param param 请求参数
@param complete 成功回调
@param failed 失败回调
*/
- (void)fetchData:(NSDictionary *)param complete:(void (^)(NSDictionary *response))complete failed:(void (^)(NSString *error))failed;
@end
接口实现类 InterfaceService.h
@interface InterfaceService : NSObject <InterfaceProtocol>
@end
接口实现类 InterfaceService.m
#import "InterfaceService.h"
@implementation InterfaceService
- (void)fetchData:(NSDictionary *)param complete:(void (^)(NSDictionary *response))complete failed:(void (^)(NSString *error))failed
{
if (param[@"id"]) {
//模拟网络请求返回成功
complete(@{@"status":@"success"});
} else {
//模拟网络请求返回失败
failed(@"error");
}
}
@end
ViewController中的代码
#import "ViewController.h"
#import "InterfaceProtocol.h"
#import "InterfaceService.h"
@interface ViewController ()
@property (nonatomic, strong) id<InterfaceProtocol> service;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//接口指向一个实现类
//其实这边可以使用DI注入
self.service = [InterfaceService new];
//发起一个请求
[self.service fetchData:@{@"id":@"1"} complete:^(NSDictionary *response) {
NSLog(@"do sth...");
} failed:^(NSString *error) {
NSLog(@"%@",error);
}];
}
@end
结束语
在今后的学习过程当中,我会继续尝试在项目里结合更多的元素,设计出更好的代码。
小伙伴们如果有好的想法可以一起讨论,以上有不足或者错误的地方,欢迎提出,我会认真回复。
网友评论