OC

写出好代码的几个要素

Posted by sunzhongliang on March 20, 2022

前言

虽说我们编写的代码最终都会编译成机器码供操作系统运行,在运行我们的代码只要不出现异常就好;那为什么还会存在软件设计架构这个话题呢?其实际上无外乎涉及到以下几个方面

  1. 代码首先是要人来阅读的,如果你的代码没有一点架构、规范、注释等等,那么阅读起来就非常困难
  2. 不好的代码,后期去修改不仅费时费力,而且还很容易产生bug
  3. 我们不能保证我们写的代码没有任何问题,如果出现问题,一个好的架构下的代码会非常容易定位到问题所在

代码块标记

善用- pragma mark

- pragma mark标记是为了便于查找和导航代码,也不要过分的使用这个标记,一般情况下,我们将属性、方法、协议、私有方法等,分组即可,如:

#pragma mark - property

- (UILabel *)userNameLabel {
    if (!_userNameLabel) {
        _userNameLabel = [[UILabel alloc] init];
    }
    return _userNameLabel;
}

#pragma mark - method

- (void)requestData {
    // do something
}

#pragma mark - UITableView delegate

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
  return 44;
}

不要去猜

不要让别人去猜你的代码要传入什么参数,也不要让别人去猜你的代码返回了什么参数

一个很好的例子:

- (instancetype)initWithSelectedArray:(NSArray *)selectedArray;

对于以上代码,如果别人要调用这个方法只知道传入一个数组,但数组里面装的是什么数据?所以一个良好的习惯是善用泛型,如:

- (instancetype)initWithSelectedArray:(NSArray<ProductModel *> *)selectedArray;

以上代码一看就知道别人要调用需要传入一个ProductModel类型的数组,在返回值上面也是同样如此

善用注释

不要在意你多打几个字会浪费时间,一个好的注释能够快速的让人明白这个是做什么的

/// 显示日期选择器
/// @param startDate 选中的开始时间
/// @param endDate 选中的结束时间
/// @param callback 回调
+ (void)showTimePicker:(NSDate *_Nullable)startDate
               endDate:(NSDate *_Nullable)endDate
               cllback:(void(^)(NSDate *startDate, NSDate *endDate))callback;

不信任原则

我们通常在调用API时,对API返回的数据都会有一个不信任原则,即返回来的数据我们在使用的时候都需要进行一个校验。
我们在写一些组件、方法同样也应当这么做。一个原则是传入过来的参数都需要进行一个合法校验,在必要时可根据场景使用return或者是throw出一个Exception

- (instancetype)initWithFrame:(CGRect)frame parentVC:(UIViewController *)parentVC childVCs:(NSArray *)childVCs {
    if (self = [super initWithFrame:frame]) {
        if (parentVC == nil) {
            @throw [NSException exceptionWithName:@"PagingView" reason:@"SGPageContentScrollView 初始化方法中所在控制器必须设置" userInfo:nil];
        }
        self.parentViewController = parentVC;
        if (childVCs == nil) {
            @throw [NSException exceptionWithName:@"PagingView" reason:@"SGPageContentScrollView 初始化方法中子控制器必须设置" userInfo:nil];
        }
        self.childViewControllers = childVCs;
        
        [self initialization];
        [self setupSubviews];
    }
    return self;
}

或者

- (void)selectProduct:(Product *)model {
    if (!model) {
        return;
    }
    // do something
}

限制方法调用

使用NS_UNAVAILABLE屏蔽掉不想让别人调用的方法

/**
 Initialize with frame and title.
 
 @param title Title of barButtonItem.
 */
-(nonnull instancetype)initWithTitle:(nullable NSString *)title NS_DESIGNATED_INITIALIZER;

/**
 Unavailable. Please use initWithFrame:title: method
 */
-(nonnull instancetype)init NS_UNAVAILABLE;

不要忽略编译器的警告

在使用自动布局的时候一些布局上的警告,类型警告,废弃api的警告….等等

遵循好的版本号

一个好的版本号应当有三部分组成,主版本号+次版本号+Bug fix版本号
15.7.112.3.7.9都是正常的写法;在发布版本时, 不兼容的版本应该主版本号加1,日常功能特性版本就在次版本号加1,bug修复就在Bug fix版本号加1
软件行业有很多包管理器都遵循这一规则,比如Cocoa Podsnpm包管理器等

break change

一个成熟的组件、库,在发布小版本迭代时,不应当有较大的改动,如果有,那么也应该有适当的兼容,比如

@property(nonatomic) CGRect contentStretch API_DEPRECATED("", ios(3.0, 6.0)) API_UNAVAILABLE(tvos); 

这种写法,通常都会伴随一个很明显的注释

// please use -[UIImage resizableImageWithCapInsets:] to achieve the same effect.

虽然你调用是没问题的(会有一个警告),但这部分代码后期可能不复存在,应当尽早的更新到新版代码上 当如果有一个较大的版本时,主版本号都应当+1,表明是一个全新的大版本,这个时候可以把一些兼容性代码给移除掉

遵循代码规范

遵循一个好的团队代码规范和命名规范,能使得在review时事倍功半。

优秀的架构

无论是开发什么应用,成本和时间都是极为重要的,较少的开发时间意味着可以尽快的上线,较低的开发成本也能帮助公司节省资金,这就要求了我们的代码要具有很优秀的架构

本文首次发布于 孙忠良 Blog, 作者 [@sunzhongliang] , 转载请保留原文链接.