不少的app使用MVC設計模式來將「用戶交互」與「數據和邏輯」分開,而model其中一個重要做用就是持久化。下文中設計的Model可能不是一個完美的,擴展性強的model範例,但在我須要重構的app中,這樣的設計可以知足個人須要。面試
Model層包含了app的數據與邏輯,Model層中的類須要關心的是數據的表現,存儲,以及操做。Model層是整個app生態中相對獨立的一個部分,由於它不會直接與controller層或者是View層進行通信,而是在其餘層須要請求它的信息的時候進行間接通信。數據庫
想要寫好一個model,首先要清晰Model的做用。設計模式
readwrite
,因此可以被改變,並保存到本地咱們能夠建立一系列的Model類,它們之間能夠互相繼承,同時每個Model類是與當前app中的實體對應。好比在我當前須要重構的app中,用戶數據對應的是UserInformationModel
,班級信息對應的是StudentClassModel
。api
另外一方面,在Model類的實現過程當中,有許多問題須要解決,因此我下面會根據我當前正在重構的app的一些狀況結合來解釋。緩存
數據能夠以各類不一樣的格式儲存,在我重構的app中,數據等信息是使用普通的數據結構來存放,好比使用數據或者是字典來保存Model的信息。在一開始創建Model的時候並無太大的問題,可是當需求不斷增長,一個Model類的信息開始變得龐大起來的時候,問題就開始浮現了。好比下面我要輸出用戶的姓名、年齡、班級、班主任、年級等數據。網絡
// never do this !!! - (void)printInformation { NSLog(@"name: %@", [user objectForKey:@"name"]); NSLog(@"age: %@"m [user objectForKey:@"age"]); NSLog(@"class: %@"m [user objectForKey:@"clazz"]); NSLog(@"teacher: %@"m [user objectForKey:@"teacher"]); NSLog(@"grade: %@"m [user objectForKey:@"grade"]); }
這樣取出數據彷佛沒什麼問題,可是當數據多起來的時候就會發現代碼會十分混亂,並且不!美!觀!,同時最主要的問題是,在從字典中取出數據的過程當中會把key打錯,致使數據取出失敗。數據結構
因此在設計Model過程當中,儘量將Model設計成一個類而不是一個結構,在類中可使用屬性(Property)來存取信息,它可以提供給開發者基本的拼寫檢查,徹底杜絕打錯key致使的數據獲取失敗的狀況,同時方便其餘開發者看到Model中存儲的數據類型,也方便往後的擴展與維護。app
// YES! do this! - (void)printInformation { NSLog(@"name: %@", user.name); NSLog(@"age: %@", user.age); NSLog(@"class: %@", user.clazz); NSLog(@"teacher: %@", user.teacher); NSLog(@"grade: %@", user.grade); }
因爲我重構的app大部分功能是基於網絡的,因此網絡數據在Model層的處理會是重點。由於網絡數據是異步獲取的,並且獲取過程很容易失敗。因此網絡數據的獲取比本地數據的獲取難度要更大。另外一方面,不少app的網絡請求框架中都提供了緩存功能,能夠在下一次請求中從緩存中更快的獲取數據,而不須要再次進行網絡請求,這裏又涉及到本地數據獲取等問題,使得網絡數據的處理顯得尤爲繁瑣。框架
在一個同步的網絡環境下,咱們能夠把錯誤處理放到其餘地方,能夠簡單的作緩存,甚至能夠像處理本地數據同樣來更新、刪除、添加新的網絡數據。但很不幸的是,網絡是異步的,因此咱們須要處理這個重要問題。異步
首先我在上文提到的緩存、失敗處理,這些應該是交給網絡請求框架來進行處理,最後獲得一個responseObject
,再對responseObject
進行model轉換等操做。
好比我在當前的項目中,是利用了MJExtension來進行字典與模型之間的轉換,而這個轉換,我是放到了每一個單獨的API中,好比對於庫存的請求,我在InventoryAPI
中進行對responseObject
轉換成InventoryModel
的操做,而不是放到vc中進行,這樣在vc中僅僅是進行網絡請求,請求完成後返回的是我想要的model。
InventoryAPI.m
- (id)modelingFormJSONResponseObject:(id)JSONResponseObject { NSUInteger count = ((NSArray *)JSONResponseObject).count; NSMutableArray *modelsArray = [NSMutableArray array]; for(int i = 0; i < count; i ++) { InventoryModel *model = [InventoryModel mj_objectWithKeyValues:JSONResponseObject[i]]; [modelsArray addObject:model]; } return modelsArray; }
InventoryViewController
- (void)requestInformation { [api startWithBlockSuccess:^(__kindof YXYBaseRequest *request) { // request.responseObject 將會返回modelsArray } failure:^(__kindof YXYBaseRequest *request, NSError *error) { }]; }
本地數據有多種方式存儲,比較常見的作法是使用.plist
文件存儲很是簡單的數據,例如設置,而會使用SQLite數據庫來存儲其餘複雜的數據。另外一方面,能夠試着使用Core Data
來對存儲數據model,雖然Core Data
會帶來更多問題,甚至會影響性能,但它的NSFetchResultsController、懶加載、數據處理工具等也是十分的好用,因此……看本身。
在本地數據處理中,最重要的是如何獲取和修改數據。在我如今須要重構的項目中並無太多的本地內容,絕大部份的數據都是經過網絡獲取,因此我並無準備詳細講對Data Model
的重構與規範化處理。但基本的原則是對每一個model配備一個存取器,裏面能夠提供fetchALl
、fetchAllUsingPredicate
、createInstance
、save
等操做,每一個model均可以使用這些操做來獲取數據,而這些操做的邏輯隱藏起來,在調用的時候我不須要知道我這個model到底是存放在數據庫中,仍是.plist
文件中,仍是在緩存中。我只須要知道當我調用這些方法的時候,我能夠獲取到我想要的model,並能夠取出我想要的數據。
在model中不僅是可以處理數據的存儲,還能夠對業務邏輯進行處理。在我須要重構的項目中,不管是強弱業務邏輯都有散落在vc的狀況,致使了vc十分臃腫,以下面的代碼中,就是須要實現從DateModel中
取出一個時間,將NSDate
格式的時間轉換到NSString
,並在View中展現出來的狀況:
DateModel.h
@interface DateModel : BaseModel @property (copy, nonatomic) NSDate *currentDate; @end
DateModel.m
- (instantcetype)init { if (self = [super init]) currentDate = [NSDate date]; return self; }
aViewController.m
- (void)showTheDate { DateModel *dateModel = [DateModel new]; NSDate *date = dateModel.currentDate; NSDateFormatter *format = [[NSDateFormatter alloc] init]; format.dateFormat = @"yyyy年MM月dd號 HH:mm:ss"; NSString *string = [format stringFromDate:date]; self.dateLabel.text = string; }
事實上這段代碼徹底能夠分開放置在model層中,讓整個vc的代碼更加清晰,如:
NSDate+dateTransform.h
@interface NSDate (dateTransform) + (NSString *)transformStringFromDate:(NSDate *)date; @end
NSDate+dateTransform.m
+ (NSString *)transformStringFromDate:(NSDate *)date { NSDateFormatter *format = [[NSDateFormatter alloc] init]; format.dateFormat = @"yyyy年MM月dd號 HH:mm:ss"; NSString *string = [format stringFromDate:date]; return string; }
建立一個NSDate
的分類並將轉換的代碼放到其中,並由DateModel調用他來完成轉換
DateModel.h
@interface DateModel : BaseModel @property (copy, nonatomic) NSString *currentDate; @end
DateModel.m
- (instantcetype)init { if (self = [super init]) { currentDate = [NSDate transformStringFromDate:[NSDate date]]; } return self; }
而後在vc中,咱們只須要簡單的調用:
aViewController.m
- (void)showTheDate { DateModel *dateModel = [DateModel new]; self.dateLabel.text = dateModel.currentDate; }
這樣整個vc的代碼結構就清晰了不少,model層取出的屬性也能直接交付到view層去顯示,而日期轉換這類代碼也能輕易的被複用。總之,針對這類的model,能夠把調用方法儘量的抽象,當須要解耦這部分代碼的時候能夠應用IOC模式,不然,在往後改變這些邏輯會變得十分困難。以上內容就是本篇的所有內容以上內容但願對你有幫助,有被幫助到的朋友歡迎點贊,評論。若是對軟件測試、接口測試、自動化測試、面試經驗交流。感興趣能夠關注我,咱們會有同行一塊兒技術交流哦。