前言
代码的写法应当使别人理解它所需的时间最小化。 ——《编写可读代码的艺术》
一个名字是一段小小的注释,虽然空间很小,但是会获得最多且最直接的注视。一个好的名字可以让接手这段代码的人快速明白上下文,一个烂名字会让接手者和调用方感到很困惑,增大协作成本。
命名理论
译事三难:信、达、雅。 ——严复
起一个好的名字也需要讲究“信达雅”:
信:含义准确
达:通顺流畅
雅:简明优雅
把信息装在名字里
起一个好名字需要有更高的信息密度,尽量选择专业的词
具体&专业
空洞四剑客:get, make, find, stop
/// 获取当前图片的大小
- (CGSize)getCurrentLayerSize;
e.g. "get"这个词过于笼统,蕴含的信息量太少了,获取一个size有很多种途径可以拿:
- 直接取:- (CGSize)currentLayerSize;
- 计算:- (CGSize)calculateCurrentLayerSize;
- 请求:- (CGSize)fetchCurrentLayerSize;
- 生成:- (CGSize)generateCurrentLayerSize;
e.g. "make"这个词太业余了,可以有更加专业的动词替换:
- 生成:- (NSArray *)generate/create/calculateListViewModels;
- 有副作用:- (void)setup/updateListViewModels;
e.g. "stop"本身没啥问题,只是可能存在更优的固定搭配:
- kill + thread
- terminate + app / runloop
- pause / remove + animation
移花接木
单词 | 替换词 |
---|---|
send | deliver, dispatch, announce, distribute, route, fire |
find | search, extract, locate, discover |
get | generate, calculate, create, fetch, extract |
show | launch(monitor), display(view), open(url, file), popup(alertController), present(viewController) |
具体具体再具体
- 尽量避免给变量取名为ret, tmp, case, num等空泛的词,请绞尽脑汁取一个语义化的名字。
- 前后缀可以丰富含义,rawContent, escapedString, stringifiedText, stayTimeMs
- 不要使用 -1 等边界量用于标记“未使用”或者“范围外”,使用NSIntegerMin, NSIntegerMax, NSNotFound
清晰和精确比装可爱好。 ——《编写可读代码的艺术》
PHP中有一个字符串处理函数叫做explode(),这是一个很有表现力的名字,描绘了将一个字符串拆成碎片的景象,但是看名字根本看不出和split()有什么区别(功能相同)。
explode -> split (by string)
split -> preg_split (by regular expression pattern)
符合预期的语义
布尔值命名
- 使用is, should, has, can, need等前缀,令bool属性更加明确
- 尽量避免使用反义前缀,例如disable,以统一语义
- 避免使用二义性词语,例如过去式和过去分词相同的词语
Example 1
@interface ChildView : UIView
@property (nonatomic, readonly, assign) BOOL isLeft;
@end
isLeft是离开了还是在左边?
修改建议:isOnLeft / isLeftPosition
Example 2
// 相册授权状态
NSString *authStatus = @"未授权";
BOOL readAssetCount = NO;
if ([AlbumPickerHelper photoLibraryAuthorizationStatus] == AuthorizationStatusAuthorized) {
authStatus = @"全部授权";
readAssetCount = YES;
} else if ([AlbumPickerHelper isPhotoLibraryLimited]) {
authStatus = @"部分授权";
readAssetCount = YES;
}
if (readAssetCount) {
// 获取相册资源数量
videoCount = [[AlbumQuickLoader sharedQuickLoader].albumResult countOfAssetsWithMediaType:AssetMediaTypeVideo];
imageCount = [[AlbumQuickLoader sharedQuickLoader].albumResult countOfAssetsWithMediaType:AssetMediaTypeImage];
}
readAssetCount是已经读取了还是需要去读取?
修改建议:
强调完成:isAccessedAssetCount
强调预期:needReadAssetCount
方法(接口)的命名
真正的命名艺术还得看我 => Code Naming Basics
- 名称自解释(self-documenting)
e.g. 一个字符串中用一个子串替换另一个子串,并返回替换后结果
cpp的写法:
- python的写法:
# newStr分别为多少
srcStr = 'abcba'
newStr1 = srcStr.replace('a', 'b')
newStr2 = srcStr.replace('a', 'b', 1)
如果名字无法自解释,点开文档看参数原型,使用成本较高,且代码可读性不佳
- objective-c的写法:
- [NSString stringByReplacingOccurrencesOfString:withString:]
方法名字虽然很长,但是读上去非常类似于日常语言里的句子:
The source string returns a new string by replacing the occurrences of the string ‘xxx’ with the string ‘yyy’.
如果一个方法名从左到右读起来狗屁不通,please rename it !
- 不要用缩写
苹果在官方文档中详细论述过为什么不要使用缩写,oc本来就是用代码空间来换理解时间的,没必要省那几个字。
它甚至还开了一个白名单 => Acceptable Abbreviations and Acronyms
- 方法名要与使用者的期望相匹配
以get为前缀的方法一般是“轻量级访问器”,若其时间复杂度较高或者篡改了属性,可能会给调用者带来困扰🤔。
calculate, compute, generate看上去是有代价的操作(非常数级时间复杂度),update, setup, clear则显得有副作用。
- 私有方法不要用_开头
这个前缀是预留给苹果公司使用的,如果使用可能会误覆写父类的私有方法,而导致错误调用
例如:UIViewController
有一个_resetViewController方法
解决方案:使用自定义前缀,例如:p_
类与对象的命名
-
后缀必须体现出其类型
子类通常需要继承父类的后缀,若子类名与父类对应不上,会给使用者带来困扰
e.g. 继承自NSObject
的model若叫做viewController,会给使用者一种错觉,比如可以挂在响应链上 -
类名需要符合使用的预期
xxxRequest: 一个发请求的Utils实例
xxxData/Info: 一个model
xxxManager/Helper/Center: 一个单例类
xxxProducer/Factory: 工厂类
xxxing/able: 协议 -
对象名也要和类名对应
UIImage *image; UIImageView *imageView; UITapGestureRecognizer *tapGestureRecognizer;
避免变成裹脚布
一个名字可以通过堆冗余的信息来显得天衣无缝,但过长的名字会很难记,屏占空间大,产生更多的换行,这违背了KISS和DRY原则
- 提炼语言:convertToInteger, doHandlerXXX -> handleXXX
- 作用域小的标识符无需携带太多信息,因为上下文较为清晰。
category名字不需要重复前缀,系统原生类需要替换前缀(UIViewController+PrefixXxx) - 不要花里胡哨
主态:hostState -> isAuthorSelf
头像框:avartarPendant -> avatarDecorator
注释是命名之友
- 当名字无法包含足够丰富的上下文信息,可以用注释去补充。
- 好名字>烂名字+注释。名字总是第一时间映入眼帘的,与其写一大段注释来解释不如改掉这个名字。
文档注释
- option+cmd+/
- /**回车
/**
@brief 取消投票
@discussion 接下来是这个方法的使用说明
@code
[self.viewModel revokeVoteWithCompletionHandler:^{
@strongify(self);
}];
@endcode
@note 超级简单是不是
@param completionHandler 结束回调
@return 这是一个返回值
*/
- (NSInteger)revokeVoteWithCompletionHandler:(HandlerType)completionHandler;
效果
枚举注释
/// 表情补充方式
typedef NS_ENUM(NSInteger, SomeEnumType) {
SomeEnumTypeDefault, ///< 默认样式
SomeEnumTypeA ///< A类型
};
相关阅读
Introduction to Coding Guidelines for Cocoa
Markup Formatting Reference