简洁的代码

转载自 : http://www.jianshu.com/p/2db0e6b6ecdb

最近在review整个项目的代码,因为代码量很大,参与开发的人很多,所以代码很多地方写得不够简洁。这里总结出一些代码片段,用来简化代码。

1、让TableView多余的Cell不可见。
原来的实现:
给TableView增加一个空的FooterView。但是当很多地方都需有这个需求时,类似的代码就重复出现。

UIView *view = [UIView new];
view.backgroundColor = [UIColor clearColor];
[tableView setTableFooterView:view];

改进的实现:
增加一个UITableView的category,这样在调用的地方只需一行就够了。

@implementation UITableView(Addtions)

- (void)hideEmptyCells
{
    UIView *view = [UIView new];
    view.backgroundColor = [UIColor clearColor];
    [self setTableFooterView:view];
}

使用

[self.tableView hideEmptyCells];

2、让TableView Cell之间的分隔线左间距为0
这个问题是iOS7之后才出现的,iOS7的解决方法到了iOS8又出问题,所以每次用到TableView, 都要给TableView设置一便,又要给TableViewCell设置一遍,代码很多很散。

//设置tableView
if ([self.tableView respondsToSelector:@selector(setSeparatorInset:)]) {
    [self.tableView setSeparatorInset:UIEdgeInsetsZero];
}
if ([self.tableView respondsToSelector:@selector(setLayoutMargins:)]) {
    [self.tableView setLayoutMargins:UIEdgeInsetsZero];
}

//设置tableviewcell
if ([cell respondsToSelector:@selector(setSeparatorInset:)]) {
    [cell setSeparatorInset:UIEdgeInsetsZero];
}

if ([cell respondsToSelector:@selector(setLayoutMargins:)]) {
    [cell setLayoutMargins:UIEdgeInsetsZero];
}

改进的实现:
给UITableView和UITableViewCell各增加一个Category方法:

//UITableView
- (void)hideSeparatorLeftInset
{
    if ([self respondsToSelector:@selector(setSeparatorInset:)])
    {
        [self setSeparatorInset:UIEdgeInsetsZero];
    }

    if ([self respondsToSelector:@selector(setLayoutMargins:)])
    {
        [self setLayoutMargins:UIEdgeInsetsZero];
    }
}
//UITableViewCell
- (void)hideSeparatorLeftInset
{
    if ([self respondsToSelector:@selector(setLayoutMargins:)]) {
        [self setLayoutMargins:UIEdgeInsetsZero];
    }
}

然后在创建UITableView和UITableViewCell的地方分别调用就好了。

//创建Table
_newsTable = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT - 64 -49) style:UITableViewStylePlain];
_newsTable.delegate = self;
_newsTable.dataSource = self;
[_newsTable hideSeparatorLeftInset];

//创建Cell
if (cell == nil) {
    cell = [[FindBigImageTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleCell];
    [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
    [cell hideSeparatorLeftInset];
}

这样做的好处:

  • 代码简洁,只要一行。因为在很多类似的页面都会出现重复的代码,所以不同的人写往往会不一致,造成bug。
  • 以后api升级,只要改一处地方即可,不用满世界的找代码然后修改。

总结:当你写代码的时候需要复制粘贴的时候,肯定是没有封装。想想能不能用Util方法和Category进行抽象,把不变的剥离出来,组成新的方法,这比复制粘贴好太多。

3、网络请求的封装
目前代码中的网络请求,是基于Http的,主要分成如下几个过程:
1、设置请求参数
2、调用封装好的http方法发出请求
3、对清求结果进行处理
典型用法如下:

 MBProgressHUD* hudProgress;
 __block int result;
 hudProgress = [[MBProgressHUD alloc] initWithWindow:[UIApplication sharedApplication].keyWindow];

hudProgress.labelText = @"XXX";

APIPackageLoginGoHeader *goHead = [[APIPackageLoginGoHeader alloc]init];
APIPackageLoginBackHeader *backHead = [[APIPackageLoginBackHeader alloc]init];
goHead.loame = @"XXXX";
goHead.paord = @"XXXX";
goHead.surce = @"XXXX
goHead.sin = @"XXXX";
goHead.versNo = @"XXXX";
[goHead makeDictionary];
NSMutableDictionary *dicBack = [[NSMutableDictionary alloc]init];[hudProgress showAnimated:NO whileExecutingBlock:^{
    result = [GMPostServer GetServerBack:SERVER_LOGIN path_Param:nil query_Param:goHead.dicGo body_Param:nil method:GM_NETWORK_METHOD_POST returnValue:dicBack];
    [backHead getBodyDataItems:dicBack];
}completionBlock:^{
    if (result == GM_POSTBACK_SUCCESS)
    {
        [UserInfoEntity shareEntity].pne = backHead.ph;
        [UserInfoEntity shareEntity].niame = backHead.name;
        [UserInfoEntity shareEntity].hasLogin = YES;
        [UserInfoEntity shareEntity].userId = [NSString stringWithFormat:@"%@",backHead.userId];
        [UserInfoEntity shareEntity].state = backHead.state;

         if ([launchOptions objectForKey:@"UIApplicationLaunchOptionsRemoteNotificationKey"] != nil) {
             self.pushInfo = [launchOptions objectForKey:@"UIApplicationLaunchOptionsRemoteNotificationKey"];
             BasicHomeViewController *basicVc = (BasicHomeViewController*)self.window.rootViewController;
             UINavigationController *viewController = (UINavigationController *)[[basicVc.tabbar.buttonData objectAtIndex:basicVc.tabbar.selectIndex] viewController];
             [viewController dismissViewControllerAnimated:NO completion:nil];
             [[PushEventManager sharedInstance] pushEvent:self.pushInfo target:viewController];

             [APService setBadge:0];
             [UIApplication sharedApplication].applicationIconBadgeNumber = 0;
         }
         [self requestUserInfo];
     }
     else
     {
         showErroMsg(backHead.errorMsg);
     }
}];

上述代码是用户登录,在ViewController里面的,有一个很大的特点:长。而且集合了参数准备,发请求,请求结果处理,还和UI相互耦合。
试想一下,用户登录不会只在一个界面里有,如果多个地方存在登录的情况,这一块代码就会多次出现,而且大致结构都差不多吧?只有参数的取值和成功失败的处理逻辑不太一样,那么就把剩下的固定的逻辑:参数复制,hud,请求放到一个方法里面:

+ (void)userLoginWithUserName:(NSString*)userName
                     password:(NSString*)password
                      success:(void(^)(APIPackageLoginBackHeader*))sucBlock
                      failure:(void(^)(APIPackageLoginBackHeader*))failBlock
                     animated:(BOOL)animated
                  loadingText:(NSString*)loadingText
                       inView:(UIView*)containerView;
{
    MBProgressHUD* hudProgress;
    __block NSInteger result;
    hudProgress = [[MBProgressHUD alloc] initWithWindow:[UIApplication sharedApplication].keyWindow];

    if (animated) {
        [containerView addSubview:hudProgress];
        [containerView bringSubviewToFront:hudProgress];
        hudProgress.labelText = loadingText;
    }

    APIPackageLoginGoHeader *goHead = [[APIPackageLoginGoHeader alloc]init];
    APIPackageLoginBackHeader *backHead = [[APIPackageLoginBackHeader alloc]init];
    goHead.logame = userName;
    goHead.pard = password;
    goHead.sodde = @"iphone";
    goHead.swn = @"ios";
    goHead.versdddo = [LCSystemUtil appVersion];
    [goHead makeDictionary];

    NSMutableDictionary *dicBack = [[NSMutableDictionary alloc]init];
    [hudProgress showAnimated:animated whileExecutingBlock:^{

        result = [GMPostServer GetServerBack:SERVER_LOGIN path_Param:nil query_Param:goHead.dicGo body_Param:nil method:GM_NETWORK_METHOD_POST returnValue:dicBack];
        [backHead getBodyDataItems:dicBack];

    }completionBlock:^{

        if (result == GM_POSTBACK_SUCCESS)
        {
            [UserInfoEntity shareEntity].phone = backHead.pe;
            [UserInfoEntity shareEntity].nikename = backHead.niccdame;
            [UserInfoEntity shareEntity].hasLogin = YES;
            [UserInfoEntity shareEntity].userId = [NSString stringWithFormat:@"%@",backHead.uddrId];
            [UserInfoEntity shareEntity].state = backHead.stcde;

            if(sucBlock){
                sucBlock(backHead);
            }

        }
        else
        {
            if (failBlock) {
                failBlock(backHead);
            }
        }
    }];
}

外部调用,只要一行代码:

[HJUserProvider userLoginWithUserName:userName
                                 password:password
                                  success:^(APIPackageLoginBackHeader *backHeader) {
                                    [self backButtonPressed:nil];
                                    [HJUserProvider requestUserInfoWithSuccess:nil failure:nil];
                                  }
                                  failure:^(APIPackageLoginBackHeader *backHeader) {
                                    [HJUIUtil showFailedMsg:backHeader.errorMsg];
                                  }
                                 animated:YES
                              loadingText:NSLocalizedString(@"Logging now...", nil)
                                   inView:self.view];

这样的做法就把网络请求从ViewController分离开了,而且没有任何的耦合。同一个接口的请求逻辑只需写一遍(之前是copy、 paste,因为代码不是唯一的,所以不同的人,不同的时间改了了其中一处,就会造成差异,引起未知的bug)。另外一个好处就是把各种请求逻辑都集中在 了一处,便于阅读和修改。

4、数据归数据,UI归UI
从本质上说,程序就分成两部分:数据和UI。从ViewController的角度来说,就是State和View。View随着State的改变而改变,所以两者应该分开使ViewController结构更加清晰。
举个例子:viewDidLoad方法,这个方法里应该写些什么?

This method is called after the view controller has
loaded its view hierarchy into memory. This method is called regardless
of whether the view hierarchy was loaded from a nib file or created
programmatically in the loadView method. You usually override this
method to perform additional initialization on views that were loaded
from nib files.

官方的建议是写视图上的额外的初始化工作,而和视图不相关的初始化工作就不该在这里写(题外话,iOS6之前,遇到内存警
告,viewDidLoad是会调多次的,所以把对象的初始化写在这里是有问题的,iOS6之后反正没发现过viewDidLoad调用多次的情况),那
么对象的初始化(状态变量的复制)应该写在哪里呢?——初始化方法!不要因为基类提供了默认的初始化方法而偷懒不写,任何一个类,除非简单的不能再简单,
都要有一个初始化方法,哪怕里面什么都没写。下面这段代码的问题就是上班部分代码应该出现在初始化方法中。

- (void)viewDidLoad {
    [super viewDidLoad];
    _newsDataAry = [[NSMutableArray alloc] init];
    _currentPage = 0;

    //获取缓存文章
    NSArray *arr = [[ArticleSqliteData shareManager] dataGetHeadArticle];
    [_newsDataAry addObject:arr];

    NSArray *arrNewsInfo = [[ArticleSqliteData shareManager] dataHomeViewIsFirstGet:YES];
    [_newsDataAry addObjectsFromArray:arrNewsInfo];

    if ([arrNewsInfo count] != 0) {
        ArticleModel *model = [arrNewsInfo lastObject];
        _isExpire = [self calculationTime:model.updateTime];
    }
    else{
        _isExpire = YES;
    }

    //上面的代码和对象的状态相关,不该出现在这里
    [self addTableView];

    [self setTitleViewWithText:@"发现"];
    [self setLeftButtonWithTitle:@"秘籍" action:@selector(showCheats)];
    [self setRightButtonWithImageName:@"find_xiala.png" action:@selector(showCatagory)];
    self.showCategory = NO;//这一行也是状态
    // Do any additional setup after loading the view from its nib.

    /**
     *  初始化分类页面
     */
    self.categoryView  = [[HJFindCategoryView alloc] initWithFrame:CGRectMake(0, 64, SCREEN_WIDTH, SCREEN_HEIGHT)];
    [self.categoryView addBorderTopLine];
    self.categoryView.hjFindCategoryViewDelegate = self;
    self.categoryView.hidden = YES;//UI应该依赖状态,而不是写死
    [[AppDelegate appDelegate].window addSubview:self.categoryView];
    [self getBannerNewsTitle];
}

5、成员方法与Util方法
一个类代码比较多的一个原因,就是加入了不该加入的成员方法所致。一个方法,之所以称为成员方法,是因为它操作了对象的状态,如果它没有操作对象的状态,拿它就跟该对象没有紧密的关系,就不能放在类里面实现。举个例子:

-(BOOL)calculationTime:(NSDate *)date
{
    NSDateFormatter *dateFormatter=[[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSDate *  senddate=[NSDate date];
    //结束时间
    NSDate *endDate = date;
    //当前时间
    NSDate *senderDate = [dateFormatter dateFromString:[dateFormatter stringFromDate:senddate]];
    //得到相差秒数
    NSTimeInterval time=[senderDate timeIntervalSinceDate:endDate];

    int days = ((int)time)/(3600*24);
    if (days < 0) {
        return YES;
    }
    else{
        return NO;
    }
}

上面这个方法和对象没什么关系,更应该抽象成为一个Util方法,可以在这个类里面调用,更可以再其他更多的地方调用。

6、功能点单一入口
在代码中,有些功能逻辑是相对固定的,但是在很多地方会反复出现。
比如登录功能,除了用户主动登录,还有在未登
录状态下使用某些需要登录的功能,都会去调用登录页面。我看了一下现在的代码,一共出现了14次调用登录页面。代码重复是一个方面,还有就是定位bug麻
烦。比如进入首页的时候莫名其妙弹出个登录页面,因为首页逻辑复杂,我得打很多断点才能定位到产生问题的代码。
还有对tabbar的操作,也是出现在了多出地方。其实只要搞清楚tabbar是哪个类管理的,然后让这个类封装出一个public的方法,控制tabbar的隐藏与否即可。

所以一个功能点我们只保留一个出口,其他地方只要调用方法即可。把相同的地方写在方法内部,不同的地方作为参数对外开放。

时间: 2024-10-09 21:07:40

简洁的代码的相关文章

写一手简洁的代码

如果有人问我,为什么开始写代码...我会毫不犹豫的告诉他,因为IDEA的代码五颜六色的,很漂亮!!是的,我热爱写代码的初衷就是因为代码好"漂亮".从第一次开始写代码,到现在已经快2年了,"漂亮"这个词一直是我对代码热爱的初衷,只不过曾经的"漂亮"只局限于代码的颜色与字体,而现在的"漂亮"已经涵盖了颜色,接口,注释,方法,类等等,总之我喜欢写代码,只是因为它"漂亮". 之前在学校的时候,我就被实验室的同学&q

vscode简洁的代码编辑器

微软去年tuichu的代码编辑神器,vscode很不辞哦,感受还不错.微软也破天荒地跨平台地支持... 支持多种语言 vscode(官方):code.visualstudio.com vscode中文(非官方):http://www.vscode.org/ 对于Win10之下的系统,默认没有安装.NET4.5故需要安装.NET4.5 .NET4.5安装 下载地址:http://pan.baidu.com/s/1boOGc83 http://pan.baidu.com/s/1c1E4FLY 密码:

简洁界面代码助手

下载地址: www.gudianxiaoshuo.com

代码的简洁与合理

缠绕的逻辑 项目中角色投影有2种状态:圆斑那种的ambient shadow,realtime shadow(但在低配下会被禁止) 在高层针对每个角色进行管理,由于两种阴影的实现是一步步出来的,并且是经过很多修改,所以代码中呈现出比较乱的情况,而且两者的逻辑是在不同的函数中实现,彼此之间互相缠绕在一起,ambient shadow为true的时候会在ambient shadow function里面禁掉realtime shadow,反之realtime shadow函数里也会做类似的事情. 这

iOS 两行代码解决数据持久化

在实际的iOS开发中,有些时候涉及到将程序的状态保存下来,以便下一次恢复,或者是记录用户的一些喜好和用户的登录信息等等. 这就需要涉及到数据的持久化了,所谓数据持久化就是数据的本地保存,将数据从内存中迁入到存储器上.网上有很多种数据持久化的方法,如实现自己实现I/O.数据库.云或则走第三方接口等等.但是有时候可能只是进行一些简单的数据存储,如用户的偏好设置.用户的sessionID等等,这时候使用上述方法便显得有点兴师动众了,现在需要一种更加轻量化的操作方式. 一.认识 NSUserDefaul

-----------------Clean Code《代码整洁之道》--------------------

-----------------------Chapter1:整洁代码---------------------- 1.<C++>程序设计语言作者——C++之父Bjarne Stroustrup 对于整洁代码的定义: 我喜欢优雅和高效的代码.代码逻辑应当直截了当,叫缺陷难以隐藏:经量减少依赖关系,使之便于维护:移居某种分层战略完善错误处理代码:性能调至最优,省的引诱别人做没规矩的优化,搞出一堆混乱来.整洁的代码只做好一件事. 2.Grady Booch,Object Oriented Ana

收集的35个 jQuery 小技巧/代码片段,可以帮你快速开发.

1. 禁止右键点击 $(document).ready(function(){     $(document).bind("contextmenu",function(e){         return false;     }); }); 2. 隐藏搜索文本框文字 Hide when clicked in the search field, the value.(example can be found below in the comment fields) $(document

css代码整理、收集

整理了一下之前用到过的css代码,实现一种效果或许有许多种写法,我这里整理了一下我个人认为兼容性比较好,结构比较简洁的代码……如有写得不对的地方敬请前辈们指点赐教一下,小弟不胜感激!此学习笔记是动态的——我日后发现有好的代码段会陆陆续续整理添加上去. css:ellipsis省略号 <style> .news{width:320px; text-overflow:ellipsis; -o-text-overflow:ellipsis; -moz-binding:url('ellipsis.xm

为什么说 2017 年你必须要学习 Go 了(多核,网络,多人协作,简单非OO,没有注解,Native,垃圾收集,代码优雅),附两个评论

为什么要学习Go Go是未来的服务端语言—?Tobias Lütke, Shopify.在过去的几年中,Golang逐步流行起来. 还有什么能比一门新语言让码农们疯狂呢? 因此,我开始学习了一段时间Golang,在这里我将告诉你为什么你也应该学习这种新语言. 在本文里我不会告诉你怎么写hello world. 我要分析计算机硬件软件的当前阶段,以解释为什么我们需要像Go这样的新语言? 硬件限制 摩尔定律正在失效 第一个具有3.0GHz时钟速度的Pentium 4处理器是由英特尔于2004年推出的