iOS中的架构及设计模式
2020-12-21 本文已影响0人
文子飞_
iOS中的三层架构、四层架构
三层架构
- 界面层 (MVC、MVP、MVVM)
- 业务层
- 数据层(网络数据&本地数据)
四层架构
- 界面层 (MVC、MVP、MVVM)
- 业务层
- 网络层
- 本地数据层
其中,界面层的架构模式又分为好多种,从最开始的MVC模式,演化到MVP,然后到现在的MVVM模式,在不断的演化过程中核心思想归根结底还是:降低各组件之间的耦合度,使得数据的流向更加清晰明了。
image.png
App目录结构
- CommonObject
- Class
- AppDelegate
- AppPages
- Login
- Home
- Server
- ViewModel
- Model
- View
- ViewController
- Me
- Protocol
- Base
- Define
- CustomUI
- Category
- Manager
- Utils
+ Data
+ Http
- Class
一、MVC(Model、View、Controller
MVC是比较直观的架构模式,最核心的就是通过Controller层来进行调控。
- Model和View永远不能相互通信,只能通过Controller传递。
- Controller可以直接与Model对话(读写调用Model),Model通过NOtification和KVO机制与Controller间接通信。
- Controller可以直接与View对话,View通过action向Controller报告事件的发生(用户点击了按钮)。Controller是View的直接数据源
优缺点 - 优点:对于混乱的项目组织方式,有了一个明确的组织方式。通过Controller来掌控全局,同时将View展示和Model的变化分开
-
缺点:愈发笨重的Controller,随着业务逻辑的增加,大量的代码放进Controller,导致Controller越来越臃肿,堆积成千上万行代码,后期维护起来费时费力。
image.png
MVC变种
优缺点:
- 优点:对Controller进行瘦身,将View内部的细节封装起来了,外界不知道View内部的具体实现
- 缺点:View依赖于Model
二、MVP(Model、View、Presenter)
我理解的MVP设计模式,是在MVC的基础上对Controller进行瘦身。Controller只是用来管理Presenter,然后由Presenter来负责Model和View之间的通信,以及View向Controller报告事件。
简单的示例代码如下
- Presenter类
// Presenter.h
#import <UIKit/UIKit.h>
@interface Presenter : NSObject
- (instancetype)initWithController:(UIViewController *)controller;
@end
// Presenter.m
#import "Presenter.h"
#import "App.h"
#import "AppView.h"
@interface Presenter() <AppViewDelegate>
@property (weak, nonatomic) UIViewController *controller;
@end
@implementation Presenter
- (instancetype)initWithController:(UIViewController *)controller
{
if (self = [super init]) {
self.controller = controller;
// 创建View
AppView *appView = [[AppView alloc] init];
appView.frame = CGRectMake(100, 100, 100, 150);
appView.delegate = self;
[controller.view addSubview:appView];
// 加载模型数据
App *app = [[App alloc] init];
app.name = @"QQ";
app.image = @"QQ";
// 赋值数据
appView.iconView.image = [UIImage imageNamed:app.image];
appView.nameLabel.text = app.name;
}
return self;
}
#pragma mark - AppViewDelegate
- (void)appViewDidClick:(AppView *)appView
{
NSLog(@"presenter 监听了 appView 的点击");
}
@end
- ViewController类
// ViewController.m
#import "ViewController.h"
#import "Presenter.h"
@interface ViewController ()
@property (strong, nonatomic) Presenter *presenter;
//@property (strong, nonatomic) OtherPresenter *presenter1;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.presenter = [[Presenter alloc] initWithController:self];
}
@end
三、MVVM(Model、View、ViewModel)
MVVM也是对Controller进行瘦身的一种策略,将业务逻辑放到ViewModel中去处理,比如发送网络请求,加载网络数据进行字典转模型操作等等。
image.png
简单的示例代码如下
- HomeVC类:负责创建和显示视图
// HomeVC.m
@interface HomeVC ()
@property (nonatomic, strong) UITableView *homeTableView;
@property (nonatomic, strong) HomeVM *homeVM;
@end
@implementation HomeVC
#pragma mark - <Life Cycle>
- (void)viewDidLoad {
[super viewDidLoad];
// RAC网络请求
@weakify(self);
[[self.homeVM.homeCommand execute:nil] subscribeNext:^(id _Nullable x) {
@strongify(self);
NSLog(@"currentThread = %@", [NSThread currentThread]);
//NSLog(@"x = %@", x);
[self.homeTableView reloadData];
}];
// ViewModel数据绑定到view上
[self.homeVM bindVMWithView:self.homeTableView];
// 订阅按钮事件
[self.homeVM.cellButtonEventSignal subscribeNext:^(UIButton *x) {
NSLog(@"x = %@", [x titleForState:UIControlStateNormal]);
}];
}
- (void)dealloc {
NSLog(@"%s", __func__);
}
#pragma mark - <Override>
- (void)createSubViews {
[super createSubViews];
[self.view addSubview:self.homeTableView];
}
- (void)addConstraints {
[super addConstraints];
[self.homeTableView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.view.mas_safeAreaLayoutGuideTop);
make.left.right.bottom.mas_equalTo(self.view);
}];
}
#pragma mark - <getter/setter>
- (HomeVM *)homeVM {
if (_homeVM == nil) {
_homeVM = [[HomeVM alloc] init];
}
return _homeVM;
}
- (UITableView *)homeTableView {
if (_homeTableView == nil) {
_homeTableView = [[UITableView alloc] init];
_homeTableView.rowHeight = 120;
_homeTableView.tableFooterView = [[UIView alloc] init];
}
return _homeTableView;
}
@end
- HomeVM类:负责homeTableView的显示和业务逻辑处理
// HomeVM.h
@interface HomeVM : NSObject<ViewModelProtocol> {
RACCommand *_homeCommand;
}
/// 网络请求RACCommand
@property (nonatomic, strong, readonly) RACCommand *homeCommand;
/// 按钮点击信号
@property (nonatomic, strong, readonly) RACReplaySubject *cellButtonEventSignal;
@end
// HomeVM.m
static NSString *HomeTableCellID = @"HomeTableCellID";
@interface HomeVM ()<UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, copy) NSArray *homeCellVMs;
@property (nonatomic, strong) HomeTableCellVM *cellVM;
@end
@implementation HomeVM {
RACReplaySubject *_cellButtonEventSignal;
}
- (RACCommand *)homeCommand {
if (_homeCommand == nil) {
_homeCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id _Nullable input) {
// RACCommand的block
return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
// RACSignal的block
NSString *homeURL = [XMGNetworkManager urlWithHome];
NSDictionary *homeParameters = [XMGNetworkManager paramWithHome];
[XMGHttpManager POST:homeURL parameters:homeParameters progress:nil success:^(NSURLSessionDataTask *task, id responseObject) {
NSLog(@"responseObject = %@", responseObject);
int code = [[responseObject objectForKey:@"code"] intValue];
if (code == 0) {
// 推荐列表
NSDictionary *recommends = [[responseObject objectForKey:@"result"] objectForKey:@"recommends"];
NSArray *courses = [recommends objectForKey:@"courses"];
courses = [[courses.rac_sequence map:^id _Nullable(id _Nullable value) {
HomeModel *model = [HomeModel yy_modelWithJSON:value];
HomeTableCellVM *cellVM = [[HomeTableCellVM alloc] init];
cellVM.model = model;
return cellVM;
}] array];
// dic->model完成
self.homeCellVMs = courses;
[subscriber sendNext:courses];
[subscriber sendCompleted];
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
[subscriber sendError:error];
}];
/*
[LFNetManager requestWithAFURL:homeURL httpMethod:Request_Type_POST params:homeParameters success:^(id result, int code) {
if (code == 0) {
// 推荐列表
NSDictionary *recommends = [[result objectForKey:@"result"] objectForKey:@"recommends"];
NSArray *courses = [recommends objectForKey:@"courses"];
courses = [[courses.rac_sequence map:^id _Nullable(id _Nullable value) {
HomeModel *model = [HomeModel yy_modelWithJSON:value];
return model;
}] array];
// dic->model完成
[subscriber sendNext:courses];
[subscriber sendCompleted];
}
} failure:^(NSError *err) {
[subscriber sendError:err];
}];
*/
// [HomeServer loadHomeList:homeParameters success:^(id _Nonnull response, int code) {
//
// } failure:^(NSError * _Nonnull error) {
//
// }];
return nil;
}];
}];
}
return _homeCommand;
}
#pragma mark - <ViewModelProtocol>
- (void)bindVMWithView:(UIView *)view {
UITableView *tableView = (UITableView *)view;
tableView.dataSource = self;
tableView.delegate = self;
[tableView registerClass:[HomeTableCell class] forCellReuseIdentifier:HomeTableCellID];
}
#pragma mark - <UITableViewDataSource/UITableViewDelegate>
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.homeCellVMs.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
HomeTableCell *cell = [tableView dequeueReusableCellWithIdentifier:HomeTableCellID forIndexPath:indexPath];
HomeTableCellVM *cellVM = self.homeCellVMs[indexPath.row];
[cellVM bindVMWithView:cell];
[cell.button addTarget:self action:@selector(cellButtonAction:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
#pragma mark - <Event>
- (void)cellButtonAction:(UIButton *)sender {
[self.cellButtonEventSignal sendNext:sender];
//[self.cellButtonEventSignal sendCompleted];
}
#pragma mark - <getter/setter>
- (RACReplaySubject *)cellButtonEventSignal {
if (_cellButtonEventSignal == nil) {
_cellButtonEventSignal = [RACReplaySubject subject];
}
return _cellButtonEventSignal;
}
@end
- HomeTableCellVM类:负责Cell的显示和业务逻辑处理
// HomeTableCellVM.h
@interface HomeTableCellVM : NSObject<ViewModelProtocol>
@property (nonatomic, strong) HomeModel *model;
@end
// HomeTableCellVM.m
@implementation HomeTableCellVM
#pragma mark - <ViewModelProtocol>
- (void)bindVMWithView:(UIView *)view {
HomeTableCell *cell = (HomeTableCell *)view;
[cell.imgView sd_setImageWithURL:[NSURL URLWithString:self.model.courseImage]];
cell.titleLabel.text = self.model.courseName;
cell.subTitleLabel.text = self.model.grade;
[cell.button setTitle:self.model.grade forState:UIControlStateNormal];
}
@end
四、三层架构
- HomeServer类:处理Home业务层 获取本地、网络列表
// HomeServer.h
@interface HomeServer : NSObject
+ (void)loadHomeList:(NSDictionary *)parameters
success:(void(^)(id response, int code))successBlock
failure:(void(^)(NSError *error))failureBlock;
@end
// HomeServer.m
#import "AppDBTool.h"
#import "LFNetManager.h"
@interface HomeServer ()
@property (nonatomic, strong) AppDBTool *appDB;
@property (nonatomic, strong) LFNetManager *appHttp;
@end
@implementation HomeServer
- (instancetype)init
{
self = [super init];
if (self) {
self.appDB = [AppDBTool new];
self.appHttp = [LFNetManager new];
}
return self;
}
+ (void)loadHomeList:(NSDictionary *)parameters
success:(void(^)(id response, int code))successBlock
failure:(void(^)(NSError *error))failureBlock {
// 获取本地数据
[AppDBTool loadLocalHomeList:@{} success:^(NSArray * _Nonnull lists) {
if (lists.count) {
successBlock(lists, 200);
} else {
// 获取网络数据
[LFNetManager requestWithAFURL:@"homeURL" httpMethod:Request_Type_GET params:parameters success:^(id result, int code) {
successBlock(result, code);
} failure:^(NSError *err) {
failureBlock(err);
}];
}
} failure:^(NSError * _Nonnull error) {
// 获取网络数据
[LFNetManager requestWithAFURL:@"homeURL" httpMethod:Request_Type_GET params:parameters success:^(id result, int code) {
successBlock(result, code);
} failure:^(NSError *err) {
failureBlock(err);
}];
}];
}
@end