最近接手一个项目,项目之前估计是实习生做的,看到很多实习生经常会出现的问题,随手摘几个出来。
不会使用间接
例子一
间接是一个很简单入门级的编程原则,但我发现很多实习生还是会犯这样的错误,比如下面的代码:
if (!isFavorite) {
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"取消收藏" style:UIBarButtonItemStyleBordered target:self action:@selector(favourite)];
} else {
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"收藏" style:UIBarButtonItemStyleBordered target:self action:@selector(favourite)];
}
这段代码很明显违反了间接原则,调用函数的代码是一样的,唯一不同的的title参数,为什么不用变量取代title?
正确的做法:
NSString *title = isFavorite ? @"收藏" : @"取消收藏";
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:title style:UIBarButtonItemStyleBordered target:self action:@selector(favourite)];
这样不是简单了很多?如果你不喜欢用三元操作符,可以用if代替,甚至你可以这样:
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:isFavorite ? @"收藏" : @"取消收藏" style:UIBarButtonItemStyleBordered target:self action:@selector(favourite)];
但我还是推荐使用变量,这样代码看起来会清晰一些。
例子二
再看另一例代码
switch (indexPath.row) {
case 0:{
cell.textLabel.text = @"我的订单";
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
break;
}
case 1:{
cell.textLabel.text = @"我的代金券";
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
break;
}
case 2:{
cell.textLabel.text = @"我的常住酒店";
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
break;
}
case 3:{
cell.textLabel.text = @"我的收藏";
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
break;
}
}
首先且不论这样的UITableView最好用Interface Builder中的StaticCell实现,如果一定要用代码,也不用这么多重复的代码,我们可以这样。
NSString *text;
UITableViewCellAccessoryType accessoryType = UITableViewCellAccessoryDisclosureIndicator;
switch (indexPath.row) {
case 0:{
text = @"我的订单";
//accessoryType=xxx;
break;
}
case 1:{
text = @"我的代金券";
break;
}
case 2:{
text = @"我的常住酒店";
break;
}
case 3:{
text = @"我的收藏";
break;
}
}
cell.textLabel.text = text;
cell.accessoryType = accessoryType;
假如accessoryType
很少改变或者不变的情况下,我们可以这样:
cell.textLabel.text = @[@"我的订单", @"我的代金券", @"我的常住酒店", @"我的收藏"][indexPath.row];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
没错,我们只需要两行代码就可以了,这样的代码是不是要清晰简洁一些?
另外,不太推荐这种用indexPath.row
的方式来做条件判断,例如你需要在中间插入一行的话,你就知道这是一个恶梦了。即使某些时候我们需要用indexPath.row
来判断,也应该尽量用变量代替。
嵌套过深
例子一
如果一个函数/循环有多个条件,不会使用continue/return退出。
-(void) someMethod{
if(conditionOne){
//do something
}else if(conditionTwo){
//do something
}else if(conditionThree){
//do something
}else if(conditionFour){
//do something
}
}
其实这个是属于编程技巧问题了,像这种情况,通常我会尽早退出,我的做法如下:
-(void) someMethod{
if(conditionOne){
//do something
return;
}
if(conditionTwo){
//do something
return;
}
if(conditionThree){
//do something
return;
}
if(conditionFour){
//do something
}
}
代码是长一点,因为每个条件都会多一个return,但如果第一个已经匹配,后面的条件再判断是没有必要的。
例子二
-(void) someMethod{
if(condition){
//复杂的处理
if(subCondition){
//More
}
//do something
}else{
//简单的处理
}
}
像这种况,我会把简单的逻辑写在前面,然后尽早退出,这样复杂的代码就不会过多的嵌套,而导致阅读困难。代码如下:
-(void) someMethod{
if(!condition){
//简单的处理
return;
}
//复杂的处理
if(subCondition){
//More
}
//do something
}
我先判断条件做完简单的处理,然后退出,再进行复杂的处理,这样可以避免过多的嵌套。
单个函数/类代码过长
有些实习生可以一个函数写数百行上千行代码,这样阅读起来很难受,要知道,代码不只是写给你一个人看的。即使是一个人写代码,过几个月你自己还要看的。即使没人看,也应该弄得漂亮一点。
一个函数20行代码就够啦,一个类500行代码就差多了
不会抽象
这个是一个非常非常典型的问题,抽象能力是一个程序员的基本功,如果无法好好的抽象,就不能应付多变的需求。要程序员既要懒又要勤,遇到类似的逻辑要抽象出来,不要犯懒就粘贴复制党,碰到不合理的地方要去重构。
下面的例子,是一个读取xml的代码
HotelListData *itemData = [[HotelListData alloc] init];
NSArray *arrA = [element elementsForName:@"Hotelid"];
itemData.hotelID = [(DDXMLElement *)[arrA objectAtIndex:0] stringValue];
NSArray *arrB = [element elementsForName:@"Hotelname"];
itemData.hotelName = [(DDXMLElement *)[arrB objectAtIndex:0] stringValue];
NSArray *arrC = [element elementsForName:@"Address"];
itemData.hotelAddress = [(DDXMLElement *)[arrC objectAtIndex:0] stringValue];
//更多这样的代码
这种做法相当不优雅,完全是copy+paster,且不说那名称为ABCD的了。为什么不把同样的操作抽象出来做为一个方法呢,而且当xml数据少了某一个节点,这样的代码有可能就会报错。
来看如何改正,首先抽象出一个方法,用来处理从xml节点读取文本内容。
-(NSString *) getNodeTextWithName: (NSString *) name element: (DDXMLElement *) element{
NSArray *list = [element elementsForName: name];
if(list.count == 0) return nil;
return [((DDXMLElement *)list.firstObject) stringValue];
}
然后
itemData = [[HotelListData alloc] init];
itemData.hotelID = [self getNodeTextWithName:@"Hotelid" element:element];
itemData.hotelName = [self getNodeTextWithName:@"Hotelname" element:element];
itemData.hotelAddress = [self getNodeTextWithName:@"Address" element:element];