架构设计
一、 架构设计原则
- 架构设计我们需要遵循上层依赖底层、下层不能依赖上层;
- 同层之间交互可通过,中间件进行解耦;
二、分层
由下到上我们可以将整个应用程序分为四层分别是:
- 与业务无关的基础层
例如数据库FMDB、AFNetworking、UIView等的一些系统类分类等。
- 与项目业务相关的通用层
存放一些与业务相关的通用类。
- 中间层
业务层通过中间层,来调用基础层以及通用业务层;各个业务层之间可通过向中间层进行依赖注入进行交互,实现各个业务层之间的解耦。
三、项目中各个模块解耦
- 使用OpenURL的方式;
- 向中间层进行依赖注入;假设A业务模块需要调用B业务模块,我们可以将B注入到之间层(中间层持有B,同时实现B定义好的交互协议方法),A通过调用中间层实现好的B协议方法来间接与B交互;
- 在A与B之间通过中间件进行交互。
四、中间层解藕示例
假设我们现在有三个页面 InfoEditVC页面用编辑用户信息、CountryVC 选择用户所属国家、CityVC 选择用户号码区号,用户在InfoEditVC页面需要先通过CountryVC页面选择国家返回给InfoEditVC页面,在将选择的国家传递给CityVC进行城市的选择返回给InfoEditVC页面,所以我们可以简单来模拟这个几个页面的交互。
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
// 通过VCCenter来访问VC时VCCenter需要实现的协议
@protocol VCCenterPro <NSObject>
// Country协议方法
+(UIViewController *)getCountryVCWithBlock:(void(^)(NSDictionary * param))black;
// CityVC协议方法
+(UIViewController *)getCityVCWithCountry:(NSString *)country andBlock:(void(^)(NSDictionary * param))black;
@end
NS_ASSUME_NONNULL_END
#import <Foundation/Foundation.h>
#import "VCCenterPro.h"
NS_ASSUME_NONNULL_BEGIN
//VCCenter作为VC交互的中间层,一个VC访问另外一个VC需要通过VCCenter来访问
@interface VCCenter : NSObject<VCCenterPro>
@end
NS_ASSUME_NONNULL_END
#import "VCCenter.h"
#import "CountryVC.h"
#import "CityVC.h"
@implementation VCCenter
+(UIViewController *)getCityVCWithCountry:(NSString *)country andBlock:(void (^)(NSDictionary *))black{
CityVC *vc = [[CityVC alloc] init];
vc.black = black;
vc.country = country;
return vc;
}
+(UIViewController *)getCountryVCWithBlock:(void (^)(NSDictionary *))black{
CountryVC *vc = [[CountryVC alloc] init];
vc.black = black;
return vc;
}
@end
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface InfoEditVC : UIViewController
@end
NS_ASSUME_NONNULL_END
#import "InfoEditVC.h"
#import "VCCenter.h"
@interface InfoEditVC ()
@property(nonatomic,strong)UIButton *countryBut;
@property(nonatomic,strong)UIButton *cityBut;
@end
@implementation InfoEditVC
- (void)viewDidLoad {
[super viewDidLoad];
UIButton *countryBut = [[UIButton alloc] initWithFrame:CGRectMake(20, 100, 100, 40)];
[countryBut setTitle:@"选择国家" forState:UIControlStateNormal];
[countryBut setTitleColor:UIColor.redColor forState:UIControlStateNormal];
[countryBut addTarget:self action:@selector(countryButAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:countryBut];
_countryBut = countryBut;
UIButton *cityBut = [[UIButton alloc] initWithFrame:CGRectMake(20, 100+60, 100, 40)];
[cityBut setTitle:@"选择城市" forState:UIControlStateNormal];
[cityBut setTitleColor:UIColor.redColor forState:UIControlStateNormal];
[cityBut addTarget:self action:@selector(cityButAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:cityBut];
_cityBut = cityBut;
// Do any additional setup after loading the view.
}
-(void)countryButAction:(UIButton *)sender{
// InfoEditVC 通过VCCenter 访问CountryVC 不依赖CountryVC
UIViewController *vc = [VCCenter getCountryVCWithBlock:^(NSDictionary * _Nonnull param) {
[self.countryBut setTitle:param[@"country"] forState:UIControlStateNormal];
}];
[self.navigationController pushViewController:vc animated:YES];
}
-(void)cityButAction:(UIButton *)sender{
// InfoEditVC 通过VCCenter 访问CityVC 不依赖CityVC
UIViewController *vc = [VCCenter getCityVCWithCountry:self.countryBut.titleLabel.text andBlock:^(NSDictionary * _Nonnull param) {
[self.cityBut setTitle:param[@"city"] forState:UIControlStateNormal];
}];
[self.navigationController pushViewController:vc animated:YES];
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface CountryVC : UIViewController
@property(nonatomic,copy)void (^black)(NSDictionary *param);
@end
NS_ASSUME_NONNULL_END
#import "CountryVC.h"
@interface CountryVC ()
@property(nonatomic,strong)UIButton *countryBut;
@end
@implementation CountryVC
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UIButton *countryBut = [[UIButton alloc] initWithFrame:CGRectMake(20, 100, 100, 40)];
[countryBut setTitle:@"中国" forState:UIControlStateNormal];
[countryBut setTitleColor:UIColor.redColor forState:UIControlStateNormal];
[countryBut addTarget:self action:@selector(countryButAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:countryBut];
_countryBut = countryBut;
}
-(void)countryButAction:(UIButton *)sender{
[self.navigationController popViewControllerAnimated:YES];
if (_black) {
_black(@{@"country":_countryBut.titleLabel.text});
}
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface CityVC : UIViewController
@property(nonatomic,copy)void (^black)(NSDictionary *param);
@property(nonatomic,copy)NSString *country;
@end
NS_ASSUME_NONNULL_END
#import "CityVC.h"
@interface CityVC ()
@property(nonatomic,strong)UIButton *cityBut;
@end
@implementation CityVC
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UIButton *countryBut = [[UIButton alloc] initWithFrame:CGRectMake(20, 100, 100, 40)];
[countryBut setTitle:_country forState:UIControlStateNormal];
[countryBut setTitleColor:UIColor.redColor forState:UIControlStateNormal];
[self.view addSubview:countryBut];
UIButton *cityBut = [[UIButton alloc] initWithFrame:CGRectMake(20, 100+60, 100, 40)];
[cityBut setTitle:@"深圳" forState:UIControlStateNormal];
[cityBut setTitleColor:UIColor.redColor forState:UIControlStateNormal];
[cityBut addTarget:self action:@selector(cityButAction:) forControlEvents:UIControlEventTouchUpInside];
_cityBut = cityBut;
[self.view addSubview:cityBut];
}
-(void)cityButAction:(UIButton *)sender{
[self.navigationController popViewControllerAnimated:YES];
if (_black) {
_black(@{@"city":_cityBut.titleLabel.text});
}
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end
由此我们在编写代码时,可以通过中间件(中间层)的方式来避免对其他具体对象的依赖,而是依赖其抽象实现开闭原则。