程序员新手半年记

文/某鸟

写在前面

从14年初正式开始自学 Objective-C(以下简称OC),3月中旬找到程序员工作,由此入行。转眼到了如今7月,已不知不觉有了半年。 作为一个非学院派的野生程序员,我始终对自己的代码水准与专业知识作自卑态度,入行越久,越知道其困难与过程的繁复。值半年之际,与大家分享些历程与感悟,还望诸位笑纳。

经验分享

更优秀的代码

在刚工作的一个多月里,自己随着代码量的提升,对 OC 的掌握也有了一个飞跃。当突然有一天,接到了新工作的时候,我想将自己的所学用在代码里,于是,我写了如下的一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#import "BCViewController.h"
#define max_limit 7
@interface BCViewController ()
@property(nonatomic)int rank;
@property(nonatomic,strong)NSMutableArray *stack;
@property(nonatomic)BOOL flag;
@end
@implementation BCViewController
- (NSMutableArray *)stack
{if(_stack==nil){
_stack=[[NSMutableArray alloc]init];
}
for (int i=0;i<max_limit;i++) {
[_stack addObject:[NSNumber numberWithInt:self.rank]];
}
return _stack;
}
- (BOOL)flag{
_flag=!_flag;
return _flag;
}
- (int)rank{
if(self.flag){
return _rank++;
}
return 0;
}
- (void)viewDidLoad{
self.rank=1;
NSLog(@"%@",[self.stack description]);
[super viewDidLoad];
}
@end

代码实现的功能并不复杂,建立一个名为 stack 的数组,以 max_limit 为宽度,生成一组单数位递增,双数位取 0 的数据。 真正工作中的实现要比这个代码更为复杂些,比如我其实不用以 flag 这样的 BOOL 值来判定单双数。 写这段代码,只是为了扩大我在写代码时想炫耀的东西,也同时放大我的错误。

  • 过度的重写 getter。当我第一次理解了 getter 与点语法的本质时,我十分迷恋使用sth.getter调用方法同时获取返回值。以利用OC语法为目的,将算法写的晦涩难懂,在当时的我看来,却是一项值得炫耀的技能。
  • 名目繁琐的调用。由于 getter 被广泛重写,我使用了属性生成的 _preperty 进行部分的赋值于取值,同时也利用 sth.setter 进行赋值。使得整篇代码名目繁乱,读起来甚至有点不接上下文的感觉。如果变量和方法再多些,就只能挨个按住 Command 点击去追根溯源了。不巧,在那份真实的代码中,我便为自己设置了这样的障碍。
  • 对于优秀代码的误解。由于将编程本身理解为了一种技术性极强的工作,认为代码本身必须要体现其技术水平,与对一个语言的掌握程度。在实际的工作中,才发现,这样的代码写作方式,恰恰是相反的。我非但没依靠自己的技术写出一份优秀的代码,反而自我局限,写出了一堆连自己看着都累的字母。
  • 代码的拓展性和复用性。在需求发生改变时,这段代码需要改动的地方颇多不说。我惊讶的发现我原来没预料到的问题,这些数据是要提供给外部的,而我恰恰重写了所有的 getter,这导致我不得不重新写方法来获得数据。以前的自作聪明不仅没有让我提高效率和代码质量,反而给自己挖了一个大坑,让自己写了很多原本不用去写的代码。

    改变发生在公司的安排下,离开了编程岗位一个月之后,重新回到岗位上。需求发生了变化,而代码也需要合并进库中。当我重新打开自己的代码后,我花了整个下午的时间,终于看懂了我当时那些愚蠢的思路。痛定思痛,我重构了我的代码。于是乎,上面的那篇代码,被我改成了下面的这个样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// BCViewController.h
#define max_array_limit 7
#define each_number_distance 2
#import <UIKit/UIKit.h>
@interface BCViewController : UIViewController
-(NSMutableArray *)createStack;
-(NSMutableArray *)createStackWithNumberDistance:(int)distance AndMaxLimit:(int)limit;
@end
//BCViewController.m
@implementation BCViewController
int rank;
-(NSMutableArray *)createStack{
NSMutableArray *array=[self createStackWithNumberDistance:each_number_distance AndMaxLimit:max_array_limit];
return array;
}
-(NSMutableArray *)createStackWithNumberDistance:(int)distance AndMaxLimit:(int)limit{
NSMutableArray *array=[[NSMutableArray alloc]init];
for (int i=0; i<limit; i++) {
if (!(i%distance)) {
[array addObject:[NSNumber numberWithInt:rank++]];
}else{
[array addObject:[NSNumber numberWithInt:0]];
}
}
return array;
}
- (void)viewDidLoad{
rank=1;
NSLog(@"%@",[[self createStack] description]);
[super viewDidLoad];
}
@end

乍一眼望去,或许要比第一段代码繁琐些,方法名字也长了,属性也没了。但仔细看的话,我想你很快就能看懂这段代码。其实所有的算法都写到了最长方法名的那个方法里,只有一个 for 循环和一个 if 判定。如果你有心情测试的话,放心吧,两段代码的实现结果是完全一样的。总结起来,我一共做了下面几件事:

  • 整理算法 其实当我在用属性 getter 进行各种计算与取值之后,我并没有真正意识到,我所需要的数据,只需要一组 for + if 的计算就可以解决问题。而我只需要写这样的计算,再看到的时候依旧只有这段计算。如果我当初就这样写的话,我根本无需花一下午的时间去确定我到底要做什么,因为计算的过程其实少的可怜。
  • 设置参数 即便是因为需求改变,但是我依然因为数字间隔不同头痛了一番。因为单双数的判定可以用 BOOL 值,但位数变多之后,BOOL 值就无法立即得到结果,如果硬要用 BOOL 值的话,就又多了一个用来计数的变量。而数组的宽度也由当初的限制变为了可变量。于是,我将所有会变化的参数设置了宏定义,并且给出了两个接口方法。对于固定数据的调用,用简易的方法就足够,而当参数变化时,则可以利用相对复杂的方法更灵活的应用了。
  • 设计方法 这是与参数相对应的。根据需求将基本算法确定后,先写出基本方法去做实现,而后所有的调用都是对这些方法的封装,类的设计也可以沿用这种思路。从正交性上来说,方法与类的嵌套表现为树形结构的垂直轮廓是一个很不错的思路,将方法的影响降至尽可能低,高集成,低耦合,再到维护代码时的解耦重构,都是有一定指导意义的。
  • 规范命名 这点我并不保证自己做的很好。毕竟我同时使用了下划线和大写驼峰命名,我只能说我是部分遵守了 OC 原生库的命名法则。之所以说部分,是因为我在命名宏参数的时候,帕斯卡命名法我使用了小写字母。但纵使我的英语拙劣,我还是希望读我代码的人一眼就能看出这个变量和方法究竟是干什么用的,需要哪些参数。因为,我也是其中之一。

    就这样,我完成了对自己代码的重构,由此,也总结出关于优秀代码的几点新的认识。

  • 易读 不要炫耀自己的技能,因为这为阅读代码带来了困难。在别人眼中得到优秀评价的前提是,他能看完这篇代码。

  • 简单 理清算法与思路,并且用最简单直观的代码描述出来。在别人眼中得到优秀评价的第二前提是,他能看懂这篇代码。
  • 实用 知道自己为了什么去写这段代码,并且尽可能优秀的解决问题。在别人眼中得到优秀评价的又一个前提是,他不想重写这篇代码。

这几乎与我最初想象编程这个神秘而高雅工作的内涵是背道而驰的,但却同时变成了我写代码的追求。其实答案很简单,我不想在回过头来看自己写的代码时,觉得自己写了一坨屎。

规范代码

说起规范代码,说得虚伪一点,这是个门面活。为了方便自己阅读,也为了让别人看着舒服,即便不是强迫症,我还是会在回过头看自己的代码同时,调整一下换行和对齐。 让代码变的规范,或者仅仅是有一个从一而终的代码风格,是一个很细碎的过程。 在刚开始写 OC 的时候,自己很喜欢在同一行里嵌套很多个 [],夹杂着点语法,然后靠自动换行来控制版面。当然了,随着代码的复杂程度增加,比如随处可见的 block,解析 JSON 时多层数组与字典的遍历与索引,这样的方式反而会给阅读带来极大的不便。 所以,有时候只不过是为了能让一大段代码规规整整的换几次行,我也会选择加一两个指针开销,然后用 temp 做前缀,等着 ARC 把他们释放掉。 所以,从自己的代码经验和代码质量来说,这段总结并不完整,也不一定优秀。只是我在自己写代码的过程中,基于自己的阅读需求而衍生出来的书写规范而已。

  • 括号 括号是编程里一个很能体现结构和层次的东西。现在的IDE没有几个不支持括号对齐的,即便乱了,也可以选择代码,右键里选择 Structure->Re-Indent 来整理格式。或者自己在书写的时候依靠Tab键与空格来控制也是可以的。这里要说的是单句的 iffor,请一定要加空格= =。别嫌麻烦,别炫耀自己的语法功底,当你这么做了,你会感谢自己的。
  • 命名 帕斯卡命名驼峰命名简单且基本,哪怕不习惯,请用英语单词吧。不知道怎么表达时我偶尔会用字典,通篇相同的命名规则会减少打断思路的次数。熟知的过渡变量可以偷懒用用str/dic/nav/VC 这样无伤大雅的缩写,但是自动补全已经充斥着整个 IDE 的今天,面对不那么容易一下想到的缩写,请写全称吧,把简写留给自己的私有方法就好,不要为难别人。对于一个程序员来说,猜需求和猜方法的结果是同样让人不爽的。在 OC 中,也有很多值得借鉴的命名方式。

    比如: UIViewhidden 属性的getter=isHidden 回调方法会利用 will/did/should 这样的词来表示执行顺序与条件这些细碎的东西,越是统一,读起来就越是通顺。

  • 段落 在 OC 中,#pragma mark - 是个好东西,可以整理你类里的方法,方便你的检索。如果不知道怎么使用的话,你可以新建一个 UINavigationController 的模板,然后查看一下代码,并在编辑区上方的路径索引中查看最末尾的方法,那对我来说是一个惊喜,希望对你来说也是。

这些东西,大多是自己随着代码量的提升自己总结出来的细节,做不到并不影响代码质量,只是做到了,会让代码看起来更舒服。就我自己而言,我始终认为,自己除了是一个代码的撰写者以外,更是一个代码的阅读者。或许是自己之前不成功的写作经历让自己有了这样的习惯,总喜欢站在观众的角度去审视自己的作品究竟合不合格。而我,不是太习惯把自己不满意的东西交付给别人。我的作品并不一定是最优秀的,但我会尽可能把它完成到我的能力上限。 直到我需要在时间、精力与完成度上取舍时,我才会考虑忽略。毕竟这是我们的工作,我无法保证永远有人耐心等着我把一篇代码写成一首节律清晰,琅琅上口的诗篇。

注释

注释是个重头戏。随着代码量的增加,我越来越频繁的写着注释,类的注释,段落的注释,方法的注释,算法与判定的注释。原因也很简单,我怕自己忘掉自己在写什么,自己想写什么。在写 OC 时,command+/ 逐渐变成了笔,橡皮,书签,和便签。 这里不得不请出 onevcat(江湖称喵神)贡献的神器 VVDocumenter 了,等会儿会为大家展示神器的爽快之处哦~ 容我献丑,为大家给上面那段代码做个注释。鄙人不才,英语底子差,用英文写注释的能力有限,所以,我写了很掉价的中文注释。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// BCViewController.h
// test
//
// Created by BifidyCAPs on 14/7/2.
// Copyright (c) 2014年 BifidyCAPs. All rights reserved.
//
// 该类提供了生成权值数组的方法
// 权值参数为生成数字间隔与数组宽度
// 可由宏定义设置固定参数
// 也可自行设置间隔与数组宽度生成
/**
* 数组宽度上限
*
*/
#define max_array_limit 7
/**
* 数字间隔
*
*/
#define each_number_distance 2
#import <UIKit/UIKit.h>
@interface BCViewController : UIViewController
/**
* 生成权值数组
* 该数组会自动调用宏定义中的预制参数
*
* @return 返回权值数组
*/
-(NSMutableArray *)createStack;
/**
* 自行传递参数生成权值数组
*
* @param distance 权值的间隔
* @param limit 数组宽度
*
* @return 返回经参数定义后的权值数组
*/
-(NSMutableArray *)createStackWithNumberDistance:(int)distance AndMaxLimit:(int)limit;
@end

只列出 .h 的注释好了,毕竟这也是我为了注释而写的注释,只是为了说明我注意的地方。平常在写注释的时候,偶尔还是会在文本上偷懒,但是格式,却是如此固定下来了。习惯了之后,这种段落注释仿佛成了一个大空行,在阅读时,反而因为格式的统一而让人觉得整齐。 需要说明的是,以上的注释是利用 VVDocumenter 写的,在安装了插件后,/// 就可以了。具体的使用方法在上文给出的 Github 链接里都有,我就不赘述了,毕竟是国人自己写的软件,阅读说明书应该没什么障碍。 这里展示一张使用了 VVDocumenter 的截图: VVDocumenter

VIM

可能用 VIM 来命名这段有点不够全面,就我自己而言,这是因为这个软件实在太具有代表性了。 记得刚开始接触 Xcode 的时候,自己的语言基础很差,只有一些 C 语言基础与杂七杂八的语言拓荒。所以 Xcode 理所应当的成为了我接触到的第一个 IDE。(当然了,直到现在,也是我主要在使用的 IDE,毕竟我还是一个只会用 OC 做一些简单开发的菜鸟程序员。) 刚开始,对于 Xcode 的一站式集成,我爱不释手,代码的自动补全让我这样的菜鸟省去了背类名和方法的大麻烦。 其实这只是因为无知而产生的错觉而已。随着到处读文献,开始发现代码不全并不是 XCode 的特色,而是所有 IDE 的标配= = 另外,那些背不下来的类和方法,在无穷无尽的代码中,还是不经意的都背下来了。而且是如臂使指的随性敲出,类似于肌肉记忆住了敲击的顺序,而不是大脑记住了拼写= =

所以,也理所当然的,我开始发现了很多 Xcode 的弱点。 因为庞大,所以臃肿,所以慢,无法想象在成堆的视图构建完成后,一不小心误点了 storyboard 的漫长等待。 随着代码长度的增加与自己写代码的熟练程度,用鼠标来回翻代码的思路打断越来越让人难以忍受。

当自己希望能够更快速,高效,不被打扰的写代码时,我决定另外找一款用来写代码的软件。 漫长的历程我就不多讲了,说说我现在的配置吧:Sublime Text 3+Xcode 其实我并没有用 VIM,而且我依旧是个 VIM 的门外汉,对 VIM 的配置和插件充满了恐惧。 我只是在对现有的编辑方式忍无可忍之后,开始学习 VIM 的快捷键与编辑方式。 然后在 Sublime 和 Xcode 上安装了相应的编辑插件。 Sublime 的 VIM 模式是内置的,只需要改 setting 的参数就行。 Xcode 则需要安装XVIM插件。

之所以用 VIM 来命名最后一段经验分享,是想从 VIM 的使用思路来总结我的工具寻找历程。 VIM 的快捷键是组合式的,每个键都有自己的功能,单一,但是细小且明确。 于是 VIM 的编辑方式更像是对编辑思路的解释执行,选择(V),当前单词 (E),删除并插入 (C)。 我不清楚这是不是最简单的方式,因为我到现在还没有记清楚VIM的所有快捷键= = 但是 VEC 这三个字母却已经变成了习惯。 这比用鼠标选中一个单词,再改写,已经省了我不少事。 再比如 dd 的删除当前行, o 和 O 的上下行插入 都是些简单却又行之有效,让人欲罢不能的功能。 否则,我也不会心甘情愿的入坑。

而对我而言所有的资源都如同工具一般,从语言到软件工具,再到编程环境里的方方面面。 让所有的工具各司其职,用合适的工具去做对应的事情。尽可能熟练的使用他们,却也不要变成工具的奴隶。

哦哦,跑题了。其实这段文字是想推荐和介绍几个软件来着。

Xcode 不解释= =

Sublime Text 同类型试过 Textmate、Atom,甚至是 AppCode。结论就两个: 打开与处理够快;快捷功能方便,拓展性强。而且作为一个视觉患者,不得不说,Sublime 的界面也很漂亮,我还专门花时间挑选了 Theme,总之现在用的很舒心。在不是很依靠 Xcode 的其它功能 (当然也包括代码补全= =),或者是阅读其它代码,甚至是一些文本文件的时候,我都会用 Sublime,确实很方便。比 VIM 好的地方在于,你即使拿它当 Notepad 用,他也是个容易上手且功能强大的 NotePad。

Git 好吧,我没用过 SVN,所以 Git 是我的必备品。好消息是 Xcode 对于 git 的支持很强大,尤其是左右双开的代码比对和修改注释,界面本身很易懂,信息量也充足。版本控制反倒没怎么感觉优秀,公司里在用 SourceTree,自己甚至没用 Xcode做过 Commit。

觉得这个段落不太好展开,所以就这么草草的结束了。以后如果有心情,或许会专门写一篇文也说不定,不过想想看,纯粹一篇推荐软件的文,不太像我的文风呢= =再说吧~