004 小重构Controller

        各类面向对象的编程中,都广泛使用MVC模式来严格地区分模型(Model)、视图(View)和控制器(Controller)。现在回头看看第一个自己编写的程序-魔兽争霸3资料查询(https://github.com/DikeyKing/),有了一些些的MVC雏形:将XIB搭配出视图(V),用控制器控制视图还有数据之间的交互(C),数据写成单独的JSON(M)。五月份完工第一个APP到现在,现在看来,问题实在是太多了。控制器中还是出现了太多关于JSON解析的代码,变得臃肿(M和C分不开),随处可见的objectForKey非常容易在key写错的时候直接崩溃(增加出错概率)。没有重用,模型就是被我写成了JSON,然后直接在程序里以JSON的形式被获取没有对象的概念。之后在实习公司做了几个项目,两个人三个月开发四个应用,三个上架,一个连续三次被拒。虽然很快速地开发,但是代码却没有达到预想,控制器中混乱不堪,到处都是JSON解析和网络相关的操作------而这还是已经使用了Story Board的情况。

         之前是五分编程,两分找bug和修改,两分查资料,一分的学习。自己可以静下心来编程的时候,变成了三四分编程、六七分看书。但是却能更好地构思代码。想到这里,想起阿里系的乌龙茶对我进行面试的时候和我说的话:企业是创造价值的地方,而不是来学习的地方。事实的确如此。乘着在学校的时候,我可以专注于学习,用大量时间阅读和深入思考,毕业之后不会再有这样的机会。当然,实习也是值得的,但是更重要的还是那些沉淀下来的东西。

        回到主题。关于控制器的编写,这几天又在实践上有了一些新的理解。依稀记得《iOS编程》(iOS Programming: The Big Nerd Ranch Guide)中关于评价的一段话,翻出来:通常情况下,和编写控制对象相比,编写存储对象的难度更高。大多数程序员可以针对某个界面写出控制器代码并构建相应的XIB文件(很多人喜欢用纯代码,笔者比较喜欢Story Board,减少代码量,让代码和视图分的更开)。但是要写出高质量的存储对象,就需要程序员有一定的经验,知道如何写出既灵活又可靠的代码,需要犯错和修正的过程积累相关经验。

        如果一个APP需要处理外部数据(网络、相机、文件系统等等),那使用MVCS(Store)就会优于MVC。用自己在写的东西作为一个简单的例子。

 

在代码没有重构之前,登录看起来是下面这个样子的。

//ViewController.m
-(void)startLoginProgress { //发起连接 NSString *urlString = @"www.exampleURL.com"; AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; [manager.responseSerializer setAcceptableContentTypes:[NSSet setWithObject:@"text/html"]]; NSDictionary *parameters = @{@"forumType": @"", @"forumKey": @"", @"sdkType": @"1", @"packageName": @"com.mobcent.newforum.app82036", @"platType": @"5", @"appName": @"", @"email": @"", @"sdkVersion": @"2.0.0", @"password": @"" }; [manager POST:urlString parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) { //获得数据 NSDictionary *dic = responseObject; //处理数据 if ([[dic objectForKey:@"rs"] boolValue] == 1) { [SVProgressHUD showSuccessWithStatus:@"登录成功"]; } else{ [SVProgressHUD showSuccessWithStatus:@"登录成功"]; } } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@"Error: %@", error); }]; }

 

 

         这段登录主要做了三个工作,发起连接,获得数据,处理数据。但是实际上,作为一个视图控制器,根本不应该关心那么多,视图控制器只管两件事情,点击发起登录,然后将结果告诉用户。

 

 

重构后视图控制器中的代码:

//发起登录,只关心登录结果。完全不关注登录的过程。
//ViewController.m
[[JHRESTEngine sharedJHRESTManager] loginWithCompletion:^(NSError *err) { if (err ==nil) { if ([[JHUserDefaults getLoginState] isEqualToString:@"YES"]) { [SVProgressHUD showSuccessWithStatus:@"登录成功"]; [self dismissViewControllerAnimated:YES completion:^{ }]; }else{ [SVProgressHUD showErrorWithStatus:@"密码错误"]; } }else{ [SVProgressHUD showErrorWithStatus:[NSString stringWithFormat:@"%@",err]]; } }]; //登录的细节(获取API,发起连接,处理返回数据) //RESTEngine.m -(id)loginWithCompletion:(void (^)(NSError *))block{ [self POST:kJHLoginURLString parameters:[JHForumAPI getParameterDic:GET_LOGIN] success:^(AFHTTPRequestOperation *operation, id responseObject) { NSDictionary *dic = responseObject; if ([[dic objectForKey:@"rs"] isEqual: @1]) { [JHUserDefaults saveLoginState:@"YES"]; block(nil); }else{ [JHUserDefaults saveLoginState:@"NO"]; block(nil); } } failure:^(AFHTTPRequestOperation *operation, NSError *error) { block(error); }]; return self; } //API.m +(NSDictionary *)getParameterDic:(int)getParameter { //具体代码 }

 

 

      代码看上去好像还复杂了一些,这是因为例子非常的简单。这个过程中,登录的细节只包含了获取API,连接发起,处理返回数据,考虑更复杂的情况,如果有大量的类似操作的一个客户端,拥有数十个类似访问的API。放在不一样的文件对于后期的维护和更新显然是困难的。正常的应用还要考虑将返回的数据处理成对应的对象供控制器去展现(这个过程中没有体现)。想要更好的用户体验,还需要一代块关于缓存的代码。这些操作如果全部都在视图控制器中而不分离出去,会导致ViewController的臃肿和失控。

      如果项目还处于初期,而且会越来越复杂,那就尽量考虑考虑将关于存储的(Store)代码分离出去吧。少量的操作,换来的确是更清晰更易读的代码和更好的可维护性。

 

 

 

posted @ 2014-09-25 12:41  Dikey  阅读(322)  评论(0编辑  收藏  举报