zoukankan      html  css  js  c++  java
  • 简洁的代码

    转载自 : 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的隐藏与否即可。

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

  • 相关阅读:
    NOI2017 游戏
    2-SAT问题的方案输出
    hdu 2433 Travel
    bzoj千题计划230:bzoj3205: [Apio2013]机器人
    bzoj千题计划229:bzoj4424: Cf19E Fairy
    hdu 6166 Senior Pan
    poj 2404 Jogging Trails
    Oracle 删除数据后释放数据文件所占磁盘空间
    安装LINUX X86-64的10201出现链接ins_ctx.mk错误
    10G之后统计信息收集后为什么执行计划不会被立马淘汰
  • 原文地址:https://www.cnblogs.com/allanliu/p/4739817.html
Copyright © 2011-2022 走看看