国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > iOS > 正文

iOS開發(fā)-去model化開發(fā)

2019-11-09 14:17:31
字體:
供稿:網(wǎng)友

文章轉(zhuǎn)載自: Sindri的小巢 原文鏈接:http://www.jianshu.com/p/2b2290fa0beb

前言

去model化是一種框架設(shè)計(jì)上的做法,其中的model并不是指架構(gòu)中的model層,套用Casa大神博客中的原文就是:

model化就是使用數(shù)據(jù)對象,去model化就是不使用數(shù)據(jù)對象。

常見的去model化做法是使用字典保存數(shù)據(jù)信息,然后提供一個reformer負(fù)責(zé)將這些字典數(shù)據(jù)轉(zhuǎn)換成View層可展示的信息,其流程圖如下:

更詳細(xì)的理論知識可以看Casa大神的去model化和數(shù)據(jù)對象。本文基于Casa大神的實(shí)踐基礎(chǔ)使用另外一種去model化的實(shí)現(xiàn)方式。

使用背景

在很早之前就看過大神的文章,不過一直沒有去嘗試這種做法。在筆者最近跳入新坑之后,總算是有了這么一次機(jī)會。需求是存在著三個非常相似的cell,但分別對應(yīng)著不同的數(shù)據(jù)model

總結(jié)三個cell都需要的展示數(shù)據(jù)包括: * 產(chǎn)品名稱 * 使用條件 * 截止日期 * 背景圖片*

此外,優(yōu)惠信息屬于第一個和第二個獨(dú)有的。現(xiàn)在這一需求存在的問題主要有這么三點(diǎn):

三種數(shù)據(jù)對象在服務(wù)器返回的屬性字段中命名差別大

這是大部分的應(yīng)用都存在的一個問題,但是本文中的數(shù)據(jù)對象有一個顯著的特點(diǎn)是它們對應(yīng)顯示的cell存在很大的相似度,可以被轉(zhuǎn)換成相似的展示數(shù)據(jù)

三種cell可以封裝成一種,卻分別對應(yīng)著不同的數(shù)據(jù)對象

這里涉及cell和數(shù)據(jù)對象的對接問題,如果cell在以后發(fā)生改變了,那么原有的數(shù)據(jù)對象是否還能適用

控制器需要在數(shù)據(jù)源方法中調(diào)配不同的cellmodel,耦合過大

這個也是常見的問題之一,通常可以考慮適用工廠模式將調(diào)配的業(yè)務(wù)分離出去,但在本文中采用去model的方式實(shí)現(xiàn)

這些問題都有可能導(dǎo)致項(xiàng)目后期維護(hù)的過程中變得難以修改,小小的需求改動都會導(dǎo)致代碼的大改。筆者的解決方式是制定cellmodel之間對應(yīng)的兩個協(xié)議,從而控制器無需理會兩者的具體類型。

實(shí)現(xiàn)

我在上一篇文章MVC架構(gòu)雜談中提到過M層的業(yè)務(wù)邏輯放在model中,雖然本文要去model化,但只是去除屬性對象,自身的邏輯處理還保留著。下面是筆者去model化的協(xié)議圖以及協(xié)議聲明屬性:

@PRotocol LXDTicketModelProtocol <NSObject>@optional@property (nonatomic, readonly) NSAttributedString * perferential;@required@property (nonatomic, readonly) NSString *backgroundImageName;@property (nonatomic, readonly) NSString * goodName;@property (nonatomic, readonly) NSString * effectCondition;@property (nonatomic, readonly) NSString * deadline;@property (nonatomic, readonly) LXDCellType type;- (instancetype)initWithDict: (NSDictionary *)dict;@end@protocol KMCTicketCellProtocol<NSObject>- (void)configurateCellWithModel: (id<LXDTicketModelProtocol>)model;@end

對于本文之中這種存在共同顯示效果的model,可以聲明一個包含多個readonly屬性的協(xié)議,讓這些模型對象在協(xié)議的getter方法中執(zhí)行數(shù)據(jù)->展示這一過程的業(yè)務(wù)邏輯,而model自身只需簡單的持有字典數(shù)據(jù)即可:

字典數(shù)據(jù) -->展示數(shù)據(jù)

LXDCouponTicketModel為例,協(xié)議的實(shí)現(xiàn)代碼如下:

// h文件@interface LXDCouponTicketModel: NSObject<LXDTicketModelProtocol>@end// m實(shí)現(xiàn)@implementation LXDCouponTicketModel{ NSDictionary * _dict;}- (NSString *)backgroundImageName{ return ([_dict[@"overdue"] boolValue] ? @"coupon_overdue" : @"coupon_common");}- (NSAttributedString *)perferential{ NSAttributedString * result = objc_getAssociatedObject(self, KMCPerferentialKey); if (result) { return result; } NSMutableAttributedString * attributedString = [[NSMutableAttributedString alloc] initWithString: @"¥" attributes: @{ NSFontAttributeName: [UIFont systemFontOfSize: 16] }]; [attributedString appendAttributedString: [[NSAttributedString alloc] initWithString: [NSString stringWithFormat: @"%g", [_dict[@"ticketMoney"] doubleValue]] attributes: @{ NSFontAttributeName: [UIFont boldSystemFontOfSize: 32] }]]; [attributedString addAttributes: @{ NSForegroundColorAttributeName: KMCCommonColor } range: NSMakeRange(0, attributedString.length)]; result = attributedString.copy; objc_setAssociatedObject(self, KMCPerferentialKey, result, OBJC_ASSOCIATION_RETAIN_NONATOMIC); return result;}- (NSString *)goodName{ return [_dict[@"goodName"] stringValue];}- (NSString *)effectCondition{ return [NSString stringWithFormat: @"· 滿%lu元可用", [_dict[@"minLimitMoney"] unsignedIntegerValue]];;}- (NSString *)deadline{ return [NSString stringWithFormat: @"· 兌換截止日期:%@", _dict[@"deadline"]];}- (LXDCellType)type{ return LXDCellTypeCoupon;}- (instancetype)initWithDict: (NSDictionary *)dict{ if (self = [super init]) { _dict = dict; } return self;}

通過讓三個數(shù)據(jù)對象實(shí)現(xiàn)這個協(xié)議,筆者將要展示的數(shù)據(jù)結(jié)果進(jìn)行統(tǒng)一。在這種情況下,封裝成單個的cell也無需關(guān)心model的具體類型是什么,只需實(shí)現(xiàn)針對單元格配置的協(xié)議方法獲取展示的數(shù)據(jù)即可:

// h文件@interface LXDTicketCell: UITableViewCell<LXDTicketCellProtocol>@end// m實(shí)現(xiàn)#define LXDCommonColor [UIColor colorWithRed: 253/255. green: 99/255. blue: 99/255. alpha: 1]@implementation LXDTicketCell- (void)configurateWithModel: (id<LXDTicketModelProtocol>)model{ UIView * goodInfoView = _goodNameLabel.superview; if ([model type] != KMCTicketTypeConvert) { [goodInfoView mas_updateConstraints: ^(MASConstraintMaker *make{ make.left.equalTo(_perferentialLabel.mas_right).offset(10); }]; } else { [goodInfoView mas_updateConstraints: ^(MASConstraintMaker *make) { make.left.equalTo(_backgroundImageView.mas_left).offset(18); }]; } [_use setTitleColor: LXDCommonColor forState: UIControlStateNormal]; _backgroundImageView.image = [UIImage imageNamed: [model backgroundImageName]]; _perferentialLabel.attributedText = [model perferential]; _effectConditionLabel.text = [model effectCondition]; _goodNameLabel.text = [model goodName]; _deadlineLabel.text = [model deadline]; [_effectConditionLabel sizeToFit]; [_goodNameLabel sizeToFit]; [_deadlineLabel sizeToFit];}@end

三個問題前兩個已經(jīng)解決了:通過協(xié)議統(tǒng)一數(shù)據(jù)對象的展示效果,這時候并不需要model保存多個屬性對象,只需要在適當(dāng)?shù)臅r候直接從字典中獲取數(shù)據(jù)并執(zhí)行數(shù)據(jù)可視化這一邏輯即可。cell也不會受限于傳入的參數(shù)類型,只需要簡單的調(diào)用協(xié)議方法獲取需要的數(shù)據(jù)即可。那么最后一個控制器的協(xié)調(diào)問題就變得簡單了:

// m實(shí)現(xiàn)@interface LXDTicketViewController ()@property (nonatomic, strong) NSMutableArray< id<LXDTicketModelProtocol> > * couponTickets;@property (nonatomic, strong) NSMutableArray< id<LXDTicketModelProtocol> > * discountTickets;@property (nonatomic, strong) NSMutableArray< id<LXDTicketModelProtocol> > * convertTickets; @end@implementation LXDTicketViewController#pragma mark -UITableViewDataSource- (UITableViewCell *)tableView: (UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath{ UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier: KMCTicketCommonCellIdentifier]; if ([cell conformsToProtocol: @protocol(LXDTicketCellProtocol)]) { [(id<LXDTicketCellProtocol>)cell configurateCellWithModel: [self modelWithIndexPath: indexPath]]; } return cell;}#pragma mark - Data Generator- (id<LXDTicketModelProtocol>)modelWithIndexPath: (NSIndexPath *)indexPath{ return self.currentModelSet[indexPath.row];}- (NSMutableArray< id<LXDTicketModelProtocol> > *)currentModelSet{ switch (_ticketType) { case KMCTicketTypeCoupon: return _couponTickets; case KMCTicketTypeDiscount: return _discountTickets; case KMCTicketTypeConvert: return _convertTickets; }}@end

當(dāng)cellmodel共同通過協(xié)議的方式實(shí)現(xiàn)交流的時候,控制器存儲的數(shù)據(jù)源也就可以不關(guān)心這些對象的具體類型了。通過泛型聲明多個數(shù)據(jù)源,控制器此時的職責(zé)僅僅是根據(jù)狀態(tài)機(jī)的改變決定使用哪個數(shù)據(jù)源來展示而已。當(dāng)然,雖然筆者統(tǒng)一了這三個數(shù)據(jù)源的類型,但是歸根到底總要根據(jù)服務(wù)器返回的json創(chuàng)建不同的數(shù)據(jù)對象存放到這些數(shù)據(jù)源中。如果把這個業(yè)務(wù)放在控制器中原本就達(dá)不到松耦合的作用,因此引入一個中間人Helper來完成這個業(yè)務(wù):

// h文件@interface LXDTicketDataHelper: NSObject+ (void)anaylseJSON: (NSString *)JSON complete: (void(^)(NSMutableArray< id<LXDTicketModelProtocol> > *)models); @end// m實(shí)現(xiàn)#import "LXDCouponTicketModel.h"#import "LXDConvertTicketModel.h"#import "LXDDiscountTicketModel.h"@implementation LXDTicketDataHelper+ (void)anaylseJSON: (NSString *)JSON complete: (void(^)(NSMutableArray< id<LXDTicketModelProtocol> > *)models){ NSParameterAssert(JSON); NSParameterAssert(complete); [LXDQueue executeInGlobalQueue: ^{ Class ModelCls = NULL; NSDictionary * jsonDict = [NSDictionary dictionaryWithJSON: JSON]; NSMutableArray< id<LXDTicketModelProtocol> > * results = @[].mutableCopy; // 使用switch簡單工廠,如果case太多時,使用繼承關(guān)系的工廠會更好 switch ((LXDModelType)[jsonDict[@"modelType"] integerValue]) { case LXDModelTypeCoupon: ModelCls = [KXDCouponTicketModel class]; break; case LXDModelTypeConvert: ModelCls = [LXDConvertTicketModel class]; break; case LXDModelTypeDiscount: ModelCls = [LXDDiscountTicketModel class]; break; } for (NSDictionary * dataDict in jsonDict[@"data"]) { id item = [(id<LXDTicketModelProtocol>)[ModelCls alloc] initWithDict: dataDict]; [result addObject: item]; } [LXDQueue executeInMainQueue: ^{ complete(result); }]; }];} @end// m實(shí)現(xiàn)#import "KMCNetworkHelper.h" @implementation LXDTicketViewController- (void)requestTickets{ // get request parameters include 'url' and 'parameters' [LXDNetworkManager POST: PATH(url) parameters: parameters complete:^(NSString * JSON, NSError * error) { // error check [LXDTicketDataHelper analyseJSON: JSON complete: ^(NSMutableArray * models) { [self.currentModelSet addObjectsFromArray: models]; }]; }];}@end

去model化之后整個項(xiàng)目的業(yè)務(wù)流程大致可以用下圖表示:

這種方式最大的好處在于控制器和視圖不再依賴于model的具體類型,這樣在服務(wù)器返回的json中修改了模型對象字段的時候,修改ModelProtocol的對應(yīng)實(shí)現(xiàn)即可。甚至在以后的版本再添加現(xiàn)金券各種其他票券的時候,只需要在Helper這一環(huán)節(jié)添加相應(yīng)的工廠即可完成改動

尾言

去model化是一種有效快捷的松耦合方式,但絕不是萬能藥。在本文的demo中不難看到筆者使用這一方式最大的原因在于多個cell之間有太多的共性而model的屬性字段全不相同。另一方面在這種設(shè)計(jì)中Helper可能會因?yàn)槟P蛯ο蟮脑黾幼兊糜纺[,需要謹(jǐn)慎使用。一個好的項(xiàng)目框架總是隨著需求改變在不斷的調(diào)整的,沒有絕對最佳的設(shè)計(jì)方案。但是嘗試使用不同的思路去搭建項(xiàng)目可以提升我們的認(rèn)知,培養(yǎng)對于開發(fā)框架設(shè)計(jì)的認(rèn)識。


發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 渝北区| 道真| 惠来县| 双牌县| 静海县| 仁布县| 榕江县| 门头沟区| 简阳市| 从江县| 贵南县| 信宜市| 新竹县| 道真| 东至县| 柏乡县| 霞浦县| 宁德市| 西平县| 康乐县| 平乡县| 登封市| 梓潼县| 峡江县| 耒阳市| 龙泉市| 华蓥市| 潢川县| 吕梁市| 九龙城区| 桐柏县| 繁峙县| 麻城市| 黎城县| 大英县| 彭阳县| 宾阳县| 德格县| 祁连县| 汝城县| 余姚市|