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. 行爲型模式:類或對象之間如何交互,及劃分責任和算法 觀察者模式、命令模式、責任鏈模式,等等

總結

  • 適合項目的纔是最好的架構

資料參考

資料下載


最怕一輩子碌碌無爲,還安慰本身平凡難得。

廣告時間

相關文章
相關標籤/搜索