这本书相信大家也多多少少看过好多次了.可我自己仅仅也只是看而已.
直到看到别人用设计模式写出优雅的代码,才发现!
这些设计模式,自己都懂....
可是从来就没有把它实际的用到代码中.项目中写的最顺手的还是MVC慢慢的项目变庞大了,或许公司就倒了....
OK,来扯一个实际场景,首先我一个展示一个视图,然后从服务器中请求数据,再把请求下来的数据,填充到视图中展示出来.就这么一个逻辑.
方案一: 经典MVC
1.Model
2.View
3.controller
方案二: MVVM
1.Model
2.View
3.viewModel
4.controller
方案三 工厂模式|组合模式,路由?
1.view {
Cells
view
}
2.controllers
3.EnterRoom
4.privateTools {
ActionTools
FormatTools
ViewTools
}
5.protocol
这一回重点讲述方案三.
View,cells,跟Controller,我就不多扯了大伙都明白.
EnterRoom 负责 跳转控制器举个例子
控制器A想push到控制器B 那么传统的是 UIViewControler *vc = [UIViewController new]; [self.navigationController pushViewController:vc animated:YES]; 按照工厂模式,路由跳转 id vc = [MTCollegeEnterRoom viewController]; [self.navigationController pushViewController:vc animated:YES]; 有没有觉得好奇?我为什么要单独写一个类来做生成控制器的事情? 好处有如下. 1.在其他控制器中,我跳转不必要导入其他的控制器头文件. 2.在跳转控制时,我不必在写生成控制器的代码. 3.减少类与类之间的耦合,如何非得关联,则加一个中间层. MTCollegeEnterRoom这个就是中间层,曾经读到一段话:没有什么事情是加一个中间层不能解决的,不过不行就再加一个. 设想一下: 工厂 - 全国代理商 -省级区域代理商 - 县级区域代理商 - 专卖店 这个不就是中间层吗?生活中例子随处可见不浪费时间了.
下面来介绍如何生成视图了对不对?
老套路老规矩 MTHeaderView *view = [MTHeaderView headerView]; self.tableView.tableHeaderView = headerView; 按照工厂模式,路由跳转 UIView *headerView = [MTViewFactory collegeMusicHeaderView]; self.tableView.tableHeaderView = headerView; 你瞧是不是很熟悉?很控制器的套路几乎一样,又是套了一个中间层. 这是时候,你估计会想到,既然没有导入头文件,那么我要用点语法怎么办? 难不成继续强转?导入头文件? NONONO! 如果你需要给属性赋值,可以在外界提供一个Dict或者模型. 利用KVC [headerView setValue:model forKey:@"model"]; 在headerView内部做赋值刷新. 我想你肯定又要问,如果有代理时间怎么办?或者block 按照工厂模式,路由跳转 UIView *headerView = [MTViewFactory collegeMusicHeaderViewDelegate:self]; self.tableView.tableHeaderView = headerView; 在方法内部 举个例子 + (UIView *) collegeMusicHeaderViewDelegate:(id<MTCollegeMusicHeaderView>)delegate{ MTCollegeMusicHeaderView *headerView = [MTCollegeMusicHeaderView mtNew]; headerView.delegate = delegate; return headerView; } block也是同理,在我项目中可以说全局用的delegate,只需要代理方法按照规范去写,我想接盘侠会很舒服的.
下面protocol
个就没有什么好说的了,在.h全部用@class,不直接导类,把每个模块的代理写在单独的一个类里面,用的时候只需要引入protocol 就好了.
介绍这个类了FormatTools
按照单一职责的思想,我们把功能模块全部细致划分.
我还是举个代码例子
先看.h
// // MTSpectralDetailsFormatter.h // LoveGuQin // // Created by fox on 2018/5/28. // Copyright © 2018年 Moemoe. All rights reserved. // #import <Foundation/Foundation.h> #import "CollegeProtocol.h" @class MTSpectralDetailsModel; @interface MTSpectralDetailsFormatter : NSObject <UITableViewDelegate,UITableViewDataSource> @property(nonatomic,weak)id <MTSpectralDetailsLoopCellDelegate>delegate; - (instancetype)initWithTableView:(UITableView *)tablewView; @property(nonatomic,strong)MTSpectralDetailsModel *detailsModel; @end
再看.m
// // MTSpectralDetailsFormatter.m // LoveGuQin // // Created by fox on 2018/5/28. // Copyright © 2018年 Moemoe. All rights reserved. // #import "MTSpectralDetailsFormatter.h" #import "MTViewFactory+College.h" #import "MTSpectralDetailsCell.h" #import "MTCollegeEnterRoom.h" #import "MTSpectralDetailsLoopCell.h" #import "MTSpectralDetailsModel.h" @interface MTSpectralDetailsFormatter ()<MTJoinWeCellDelegate> @property(nonatomic,strong)UITableView *tableView; @end @implementation MTSpectralDetailsFormatter - (instancetype)initWithTableView:(UITableView *)tableView { if (self = [super init]) { self.tableView = tableView; [tableView registerClass:[MTSpectralDetailsCell class] forCellReuseIdentifier:NSStringFromClass([MTSpectralDetailsCell class])]; [tableView registerClass:[MTSpectralDetailsLoopCell class] forCellReuseIdentifier:NSStringFromClass([MTSpectralDetailsLoopCell class])]; } return self; } - (void)setDetailsModel:(MTSpectralDetailsModel *)detailsModel { _detailsModel = detailsModel; [_tableView reloadData]; } - (nonnull UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath { if (indexPath.row == 0) { MTSpectralDetailsCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([MTSpectralDetailsCell class]) forIndexPath:indexPath]; cell.titleString = self.detailsModel.data.content; return cell; }else { MTSpectralDetailsLoopCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([MTSpectralDetailsLoopCell class]) forIndexPath:indexPath]; cell.dataArray = self.detailsModel.data.scans; cell.delegate = self.delegate; return cell; } } - (NSInteger)tableView:(nonnull UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return 2; } - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } #pragma mark - ********* College Delegate ********* - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.row == 0) { CGFloat height = [self.detailsModel.data.content sizeForFont:[UIFont systemFontOfSize:15] size:CGSizeMake(kScreenWidth - 48, 0) mode:0].height; return height; }else { return 200; } } - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { return 0; } - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { return 0; } - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { return nil; } - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { return nil; } @end
发现没有,这个好处不需要我多说了吧.这一项,我已经把tableView的一系列相关代码移到这里来了.
这个时候再去看对应的控制器代码就简洁了..
// // MTSpectralController.m // LoveGuQin // // Created by fox on 2018/5/26. // Copyright © 2018年 Moemoe. All rights reserved. // #import "MTSpectralController.h" #import "MTViewFactory+Music.h" #import "MTSpectralFormatter.h" #import "MTSpectralRequest.h" #import "MTSpectralModel.h" #import "MTCollegeEnterRoom.h" @interface MTSpectralController ()<MTMainMusicNavSearchViewDelegate,YTKRequestDelegate> @property(nonatomic,strong)UIView *searchView; @property(nonatomic,strong)UITableView *tableView; @property(nonatomic,strong)MTSpectralFormatter *formatter; @property(nonatomic,strong)NSArray <MTSpectraldataModel *>*dataArray; @end @implementation MTSpectralController - (void)moeupNavigation { [self hideNav:YES withAnimation:NO]; [self mtNavTransparent]; } - (void)moeupSubview { self.searchView = [MTViewFactory searchVeiw:self withPlaceholdString:@"搜索琴曲" WithStyle:MTNavSearchReturn]; [self.view addSubview:self.searchView]; [self.view addSubview:self.tableView]; } - (void)moeupData { MTSpectralRequest *request = [[MTSpectralRequest alloc]init]; request.delegate = self; [request start]; } #pragma MARK -- YTKRequestDelegate - (void)requestFinished:(__kindof YTKBaseRequest *)request { MTSpectralModel *model = [MTSpectralModel yy_modelWithDictionary:request.responseObject]; if ([model.code integerValue] == 1) { self.dataArray = model.data; self.formatter.dataArray = self.dataArray; }else { [MBProgressHUD MT_ShowError:model.message]; } } - (void)requestFailed:(__kindof YTKBaseRequest *)request { NSString *error = request.error.localizedDescription; [MBProgressHUD MT_ShowError:error]; } - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; self.searchView.frame = CGRectMake(24 *kScale, MT_ASPECT_STATUS_BAR_H + 15, kScreenWidth - 48 *kScale, 44); self.tableView.frame = CGRectMake(0, self.searchView.bottom + 20 , MT_SCREEN_WIDTH, MT_SCREEN_HEIGHT - self.searchView.bottom - 20); } #pragma MARK -- MTMainMusicNavSearchViewDelegate - (void)musicNavSearchViewTextFieldDidBeginEditing:(UITextField *)textField searchView:(MTMainMusicNavSearchView *)searchView { [textField resignFirstResponder]; [self.navigationController pushViewController:[MTCollegeEnterRoom searchMainController] animated:YES]; } - (void)musicNavSearchViewSearchClickReturn { [self mtPopback]; } - (UITableView *)tableView{ if (!_tableView) { _tableView = [[UITableView alloc]initWithFrame:CGRectZero style:UITableViewStyleGrouped]; _tableView.delegate = self.formatter; _tableView.dataSource = self.formatter; _tableView.showsVerticalScrollIndicator = NO; _tableView.showsHorizontalScrollIndicator = NO; _tableView.backgroundColor = [UIColor whiteColor]; _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; } return _tableView; } - (MTSpectralFormatter *)formatter{ if (!_formatter) { _formatter = [[MTSpectralFormatter alloc]initWithTableView:_tableView]; _formatter.dataArray = self.dataArray; } return _formatter; } @end
最后一个ActionTools
这个相信就不需要我多讲了吧?
这个自行斟酌,有些可以你写个代理放到控制器.
如果是纯点击跳转,可以独立写个类ActionTools
最后一个提示,cell其实也是可以复用的.比如说cell是容器,但是可以把它里面的东西写成一个XIB或者代码自定义View,这样,你拿View去到处复用,不是很happy?
一个工程都按照如上套路写,工程再大又何妨?
iOS很简单,也很难.入门简单,当知道的越多的时候,越感到无知......
OK.写了一个半个早上.如果对你有用,点个👍,或者打赏可好?嘿嘿!😜