iOS底层原理 MVC、MVP、MVVM、分层设计浅谈 — (13)

上篇文章讲了内存管理,这篇文章主要讲解关于架构的一些思考,经过这篇文章你将了解到git

  1. MVC
  2. MVC变种
  3. MVP
  4. MVVM
  5. 分层设计的优缺点

没有最好的架构,只有最适合业务的架构。github

MVC

苹果版本的MVCModelVC和交互,VCView交互算法

  • 优势:ViewModel能够重复利用,能够独立使用数据库

  • 缺点:Controller的代码过于臃肿编程

代码:设计模式

- (void)viewDidLoad {
    [super viewDidLoad];
    [self loadData];
}
- (void)loadData{
    self.data=[NSMutableArray array];
    for (int i = 0; i < 20; i ++) {
        FYNews *item=[FYNews new];
        item.title =[NSString stringWithFormat:@"title-%d",i];
        item.name =[NSString stringWithFormat:@"name-%d",i];
        [self.data addObject:item];
    }
}


- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.data.count;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];
    
    // Configure the cell...
    FYNews *item =[self.data objectAtIndex:indexPath.row];
    cell.detailTextLabel.text =item.title;
    cell.textLabel.text = item.name;
    return cell;
}

//model

@interface FYNews : NSObject
@property (nonatomic,copy) NSString *title;
@property (nonatomic,copy) NSString *name;
@end
复制代码

这里是VC中组装了tableviewmodel的数据在VC中在view中显示出来,当须要另外的数据的时候,只须要将model改为须要的model而无需更改tableview的代码兼容性较好。bash

MVC变种

MVC变种,其实就是将modelview创建了联系,view依据Model来展现数据,VC组装Model,组装展现是在view中实现。网络

  • 优势:对Controller进行瘦身,将View的内部细节封装起来了,外界不知道View内部的具体实现架构

  • 缺点:view依赖于Modelmvc

代码实现

//.h
@class FYItemModel;
@interface FYAppleView : UIView
@property (nonatomic,strong) FYItemModel *model;
@end

//.m
@interface FYAppleView()
@property (nonatomic,strong) UILabel *nameLabel;
@end

@implementation FYAppleView
-(instancetype)initWithFrame:(CGRect)frame{
    if (self =[super initWithFrame:frame]) {
        _nameLabel=[[UILabel alloc]initWithFrame:CGRectMake(0, 0, 100, 30)];
        [self addSubview:_nameLabel];
    }
    return self;
}
/*
  mvc的变种
 */
- (void)setModel:(FYItemModel *)model{
    _model = model;
    _nameLabel.textColor = model.bgColor;
    _nameLabel.text = model.name;
}
@end

//FYItemModel
@interface FYItemModel : NSObject
@property (nonatomic,copy) NSString *name;
@property (nonatomic,strong) UIColor *bgColor;
@end


//ViewController
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self loadViewOtherMVC];
}
//变种MVC 把View和Model创建起链接
//等之后更新view数据只须要 view.model = item;Controllr少了许多代码
- (void)loadViewOtherMVC{
    FYAppleView * view =[[FYAppleView alloc]initWithFrame:CGRectMake(200, 200, 100, 30)];
    FYItemModel *item=[[FYItemModel alloc]init];
    item.name = @"校长来了";
    item.bgColor = [UIColor redColor];
    view.model = item;
    [self.view addSubview:view];
}
@end
复制代码

能够看到model组装到view展现内容是在view实现的,外部不知道细节,只须要将modelview便可,可是只能传输过来model或者他子类,业务更改的话,须要修改view的内部model才能将变动过的数据从新展现出来。

想要监听view的点击事件来作一些操做,那么咱们可使用代理和block,这里id是实现了FYAppleViewProtocol协议的,weak修饰防止循环引用,使用协议实现了和VC的通讯。

@class FYAppleView;
@protocol FYAppleViewProtocol <NSObject>
- (void)FYAppleViewDidClick:(FYAppleView*)view;
@end

@class FYItemModel;
@interface FYAppleView : UIView
@property (nonatomic,strong,readonly) UILabel *nameLabel;
@property (nonatomic,weak) id<FYAppleViewProtocol> delegate;
@property (nonatomic,strong) FYItemModel *model;
@end
复制代码

稍做更改仍是apple-MVC

// .h
@class FYItemModel;
@interface FYAppleView : UIView
@property (nonatomic,strong,readonly) UILabel *nameLabel;
@end
复制代码

View属性nameLabel暴露出来,可是不容许外界进行更改,去掉model则是MVC

MVP

MVPMVC很像,只是将VC换成了PresentervcPresent作的事情基本一致,将viewModel通讯改到了都和Presenter通讯。

代码

//MVP
//.h
@interface FYNewsPresenter : NSObject

@property (nonatomic,weak) UIViewController *vc;
//初始化
- (void)setup;
@end

.m
#import "FYNewsPresenter.h"
@interface FYNewsPresenter()<FYAppleViewProtocol>
@end

@implementation FYNewsPresenter
- (void)setup{
	FYAppleView * view =[[FYAppleView alloc]initWithFrame:CGRectMake(200, 200, 100, 30)];
	FYItemModel *item=[[FYItemModel alloc]init];
	item.name = @"校长来了";
	item.bgColor = [UIColor redColor];
	view.model = item;
	[self.vc.view addSubview:view];
}
- (void)FYAppleViewDidClick:(FYAppleView *)view{
	NSLog(@"点击了我");
}
@end


//VC中
@interface ViewController ()
@property (nonatomic,strong) FYNewsPresenter *presenter;
@end

- (void)viewDidLoad {
    [super viewDidLoad];
	_presenter=[FYNewsPresenter new];
	_presenter.vc = self;
	[_presenter setup];
}
@end
复制代码

再次对VC进行了瘦身,将更多的业务逻辑搬到了FYNewsPresenter处理,其实所有搬过去,意义比不大,FYNewsPresenter也会臃肿,也会出现和VC同样的困惑。

MVVM

MVVM是将FYNewsPresenter都搬到了FYNewsViewModel中,而后对FYNewsViewModelView进行了一个双向绑定,双向绑定可使用代理,block或者KVO实现。

代码实现

@interface FYNewsViewModel : NSObject

@property (nonatomic,copy) NSString *name;
@property (nonatomic,strong) UIColor *bgColor;

@property (nonatomic,weak) UIViewController *vc;

- (instancetype)initWithController:(UIViewController *)vc;
@end



#import "FYNewsViewModel.h"
@interface FYNewsViewModel()<FYAppleViewProtocol>


@end
@implementation FYNewsViewModel
- (instancetype)initWithController:(UIViewController *)vc{
    if (self =[super init]) {
        self.vc = vc;
        
        FYAppleView * view =[[FYAppleView alloc]initWithFrame:CGRectMake(100, 200, 100, 50)];
        //    view.model = item;
        view.delegate = self;
        view.viewModel = self; //创建kvo
        
        view.backgroundColor = [UIColor lightGrayColor];
        [vc.view addSubview:view];
        
        
        
        FYItemModel *item=[[FYItemModel alloc]init];
        item.name = @"校长来了";
        item.bgColor = [UIColor redColor];
        
        self.name = item.name;
        self.bgColor = item.bgColor;
    }
    return self;
}
- (void)FYAppleViewDidClick:(FYAppleView *)view{
	NSLog(@"点击了我");
}
@end
复制代码

view实现

@class FYAppleView,FYNewsViewModel;
@protocol FYAppleViewProtocol <NSObject>

- (void)FYAppleViewDidClick:(FYAppleView*)view;

@end

@class FYItemModel;

@interface FYAppleView : UIView
@property (nonatomic,strong,readonly) UILabel *nameLabel;

@property (nonatomic,weak) id<FYAppleViewProtocol> delegate;
@property (nonatomic,weak) FYNewsViewModel *viewModel;

@property (nonatomic,strong) FYItemModel *model;
@end


@interface FYAppleView()
@property (nonatomic,strong) UILabel *nameLabel;
@end
@implementation FYAppleView
-(instancetype)initWithFrame:(CGRect)frame{
    if (self =[super initWithFrame:frame]) {
        _nameLabel=[[UILabel alloc]initWithFrame:CGRectMake(0, 0, 100, 30)];
        [self addSubview:_nameLabel];
    }
    return self;
}
/*
  mvc的变种
 */
- (void)setModel:(FYItemModel *)model{
    _model = model;
    _nameLabel.textColor = model.bgColor;
    _nameLabel.text = model.name;
	
 
}

- (void)setViewModel:(FYNewsViewModel *)viewModel{
    _viewModel = viewModel;
   [_viewModel addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
   //使用FBKVO实现 或者本身使用KVO实现
//    __weak typeof(self) waekSelf = self;
//    [self.KVOController observe:viewModel keyPath:@"name"
//                        options:NSKeyValueObservingOptionNew
//                          block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary<NSKeyValueChangeKey,id> * _Nonnull change) {
//        waekSelf.nameLabel.text = change[NSKeyValueChangeNewKey];
//    }];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    if ([keyPath isEqualToString:@"name"]) {
        self.nameLabel.text = change[NSKeyValueChangeNewKey];
    }
}

//添加点击事件
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
	if ([self.delegate respondsToSelector:@selector(FYAppleViewDidClick:)]) {
		[self.delegate FYAppleViewDidClick:self];
	}
}

-(void)dealloc{
    [_viewModel removeObserver:self
                    forKeyPath:@"name"];
}
@end
复制代码

使用KVO或者FBKVO或者RAC都是能够的,本章节例子给出了FBKVO或者本身使用KVO的实现。

分层设计

三层架构:

三层架构(3-tier architecture) 一般意义上的三层架构就是将整个业务应用划分为:界面层(User Interface layer)、业务逻辑层(Business Logic Layer)、数据访问层(Data access layer)。区分层次的目的即为了“高内聚低耦合”的思想。在软件体系架构设计中,分层式结构是最多见,也是最重要的一种结构。微软推荐的分层式结构通常分为三层,从下至上分别为:数据访问层、业务逻辑层(又或称为领域层)、表示层

  • 目的: “高内聚,低耦合”的思想

  • 优势: 下降层与层之间的依赖 标准化

  • 缺点: 系统架构复杂,不适合小型项目

三层原理

3个层次中,系统主要功能和业务逻辑都在业务逻辑层进行处理。 所谓三层体系结构,是在客户端与数据库之间加入了一个中间层,也叫组件层。这里所说的三层体系,不是指物理上的三层,不是简单地放置三台机器就是三层体系结构,也不只仅有B/S应用才是三层体系结构,三层是指逻辑上的三层,即把这三个层放置到一台机器上。

三层体系的应用程序将业务规则、数据访问、合法性校验等工做放到了中间层进行处理。一般状况下,客户端不直接与数据库进行交互,而是经过COM/DCOM通信与中间层创建链接,再经由中间层与数据库进行交互。

三层架构中主要功能与业务逻辑通常要在业务逻辑层进行信息处理和实现,其中三层体系架构中的客户端和数据库要预设中间层,成为组建层。三层架构中的三层具备必定的逻辑性,便是将三层设置到同一个计算机系统中,把业务协议、合法校验以及数据访问等程序归置到中间层进行信息处理,通常客户端没法和数据库进行数据传输,主要是利用COM/DCOM通信和中间层构建衔接通道,实现中间层与数据库的数据传输,进而实现客户端与是数据库的交互

MVCMVVMMVP属于界面层, 当业务复杂,网络请求和db操做达到了一个新的高度,界面复杂到须要好多人来作,那么界面、业务、数据须要分层了

分层以后,获得了一个三层架构或四层架构

三层架构

数据层也能够分为两层,分为网络请求和db层。

四层架构

具体在工程中咱们一般这样体现

vc中获取数据

@interface ViewController ()
@property (nonatomic,strong) FYDBPool *db;
@property (nonatomic,strong) FYHttpPool *http;
@end

@implementation ViewController
- (void)viewDidLoad {
	[super viewDidLoad];

	//当有业务层
	[[FYNewsService new] loadNewsWithInfo:nil success:^(NSArray * _Nonnull) {
		
	} fail:^{
		
	}];
	//当没有有业务层
	self.db=[FYDBPool new];
	self.http=[FYHttpPool new];
	[self.db loadNewsWithInfo:@{} success:^(NSArray * _Nonnull ret) {
		if ([ret count]) {
			NSLog(@"数据获取成功");
		}else{
			[self.http loadNewsWithInfo:@{} success:^(NSArray * _Nonnull ret) {
				NSLog(@"数据获取成功");
			} fail:^{
				NSLog(@"数据获取失败");
			}];
		}
	} fail:^{
		[self.http loadNewsWithInfo:@{} success:^(NSArray * _Nonnull ret) {
			NSLog(@"数据获取成功");
		} fail:^{
			NSLog(@"数据获取失败");
		}];
	}];
}

复制代码

在业务层

@interface FYNewsService ()
@property (nonatomic,strong) FYDBPool *db;
@property (nonatomic,strong) FYHttpPool *http;

@end
@implementation FYNewsService
-(instancetype)init{
	if (self = [super init]) {
		self.db=[FYDBPool new];
		self.http=[FYHttpPool new];
	}
	return self;
}
- (void)loadNewsWithInfo:(NSDictionary *)info
				 success:(succcessCallback )succblock
					fail:(dispatch_block_t)failBlock{
	[self.db loadNewsWithInfo:info success:^(NSArray * _Nonnull ret) {
		if ([ret count]) {
			succblock(ret);
		}else{
			[self.http loadNewsWithInfo:info success:^(NSArray * _Nonnull ret) {
				succblock(ret);
			} fail:failBlock];
		}
	} fail:^{
		[self.http loadNewsWithInfo:info success:^(NSArray * _Nonnull ret) {
			succblock(ret);
		} fail:failBlock];
	}];
}
@end
复制代码

在db层

typedef void(^succcessCallback)(NSArray *);
@interface FYDBPool : NSObject
- (void)loadNewsWithInfo:(NSDictionary *)info
				 success:(succcessCallback )succblock
					fail:(dispatch_block_t)failBlock;
@end
复制代码

在网络请求层

typedef void(^succcessCallback)(NSArray *);
@interface FYHttpPool : NSObject
- (void)loadNewsWithInfo:(NSDictionary *)info
				 success:(succcessCallback )succblock
					fail:(dispatch_block_t)failBlock;
@end
复制代码

分层目的是瘦身,逻辑清晰,业务清晰,下降耦合,当某一块足够复杂时候,均可以进行分层,不局限于网络或db,当db足够复杂,也须要进行一个分层来解决复杂调用和处理的问题。 不一样的人来处理不一样的分层,相互影响也比较小,下降耦合。

当逻辑层足够完善,则UI层如何变更都不须要更改逻辑层。

后记

优雅的代码老是伴随着各类传统设计模式的搭配

设计模式

设计模式(Design Pattern) 是一套被反复使用、代码设计经验的总结 使用设计模式的好处是:可重用代码、让代码更容易被他人理解、保证代码可靠性 通常与编程语言无关,是一套比较成熟的编程思想

设计模式能够分为三大类

  1. 建立型模式:对象实例化的模式,用于解耦对象的实例化过程 单例模式、工厂方法模式,等等

  2. 结构型模式:把类或对象结合在一块儿造成一个更大的结构 代理模式、适配器模式、组合模式、装饰模式,等等

  3. 行为型模式:类或对象之间如何交互,及划分责任和算法 观察者模式、命令模式、责任链模式,等等

总结

  • 适合项目的才是最好的架构

资料参考

资料下载


最怕一辈子碌碌无为,还安慰本身平凡难得。

广告时间

相关文章
相关标签/搜索