重构的技巧

原文:http://www.cocoachina.com/industry/20140816/9397.html

我想一条童子军的军规:“始终保持露营地比你发现它的时候还要干净”。如果你在地上发现了一点脏东西,不管是谁弄的,都清理掉它。要为了下一拨来露营的人改善环境。(实际上,那条规矩的早期版本,出自Robert Stephenson Smyth Bden-Powell,童子军活动之父,说的是“努力使世界比你发现它时变得更好”。) 这就是为什么我不断的重构我的代码让它干净整洁. 当谈到编程的质量时,代码的可读性是我最关注的部分. 我想分享一些大部分人不知道的我用来简化代码的重构技巧.

字典的映射

假如你有一个switch语句来分配一些值:

  1. switch(condition) {
  2. case value1:
  3. result = @"valueFor1";
  4. break;
  5. case value2:
  6. result = @"valueFor2";
  7. break;
  8. case value3:
  9. result = @"valueFor3";
  10. break;
  11. case value4:
  12. result = @"valueFor4";
  13. break;
  14. case value5:
  15. result = @"valueFor5";
  16. break;
  17. default:
  18. result = @"valueForDefault";
  19. break;
  20. }
  21. return result;

记住这儿仅仅就 switch 5个值, 想象一下一对多. 让我们使用字典映射来简化这段代码:

  1. static NSDictionary *mapping = nil;
  2. if(!mapping) {
  3. mapping = @{
  4. @(value1) : @"valueFor1",
  5. @(value2) : @"valueFor2",
  6. @(value3) : @"valueFor3",
  7. @(value4) : @"valueFor4"
  8. @(value5) : @"valueFor5"
  9. };
  10. }
  11. return mapping[@value] ?: @"valueForDefault";

Pro’s(赞成的原因)

1.更方便阅读,即使在原来的switch里面有着更多的值也可以变得更容易阅读.

2.更快,映射仅仅构造一次,然后我们只需要快速的查找值.

3.更不易出错,因为这儿并不需要写break或者return. 4.基于代码映射的性质可以非常轻松的将这个映射转成某种静态数据,例如JSON,PLIST文件,.

用block动态映射

关于更加复杂的switch,怎么样真正动态的做一些事情?我们可以用block来简化这段代码.

最近,我重构了一些代码,字符串格式化来针对不同的类型:

  1. if ([title isEqualToString:@"Scratches"])
  2. {
  3. title = [NSString stringWithFormat:(self.vehicle.numberOfScratches == 1 ? @"%d Scratch" : @"%d Scratches"), self.vehicle.numberOfScratches];
  4. }
  5. else if ([title isEqualToString:@"Dents"])
  6. {
  7. title = [NSString stringWithFormat:(self.vehicle.numberOfDents == 1 ? @"%d Dent" : @"%d Dents"), self.vehicle.numberOfDents];
  8. }
  9. else if ([title isEqualToString:@"Painted Panels"])
  10. {
  11. title = [NSString stringWithFormat:(self.vehicle.numberOfPaintedPanels == 1 ? @"%d Painted Panel" : @"%d Painted Panels"), self.vehicle.numberOfPaintedPanels];
  12. }
  13. else if ([title isEqualToString:@"Chips"])
  14. {
  15. title = [NSString stringWithFormat:(self.vehicle.numberOfChips == 1 ? @"%d Chip" : @"%d Chips"), self.vehicle.numberOfChips];
  16. }
  17. else if ([title isEqualToString:@"Tires"])
  18. {
  19. title = [NSString stringWithFormat:(self.vehicle.numberOfTires == 1 ? @"%d Tire" : @"%d Tires"), self.vehicle.numberOfTires];
  20. }
  21. else title = nil;

通过使用 block 映射,我们可以重构这段代码为下面这样:

  1. static NSDictionary *titleMapping = nil;
  2. if (!titleMapping) {
  3. NSString *(^const format)(NSUInteger, NSString *, NSString *) = ^(NSUInteger value, NSString *singular, NSString *plural) {
  4. return [NSString stringWithFormat:@"%d %@", value, (value == 1 ? singular : plural)];
  5. };
  6. titleMapping = @{
  7. @"Scratches" : ^(MyClass *target) {
  8. return format([target numberOfScratches], @"Scratch", @"Scratches");
  9. },
  10. @"Dents" : ^(MyClass *target) {
  11. return format([target numberOfDents], @"Dent", @"Dents");
  12. },
  13. @"Painted Panels" : ^(MyClass *target) {
  14. return format([target numberOfPaintedPanels], @"Painted Panel", @"Painted Panels");
  15. },
  16. @"Chips" : ^(MyClass *target) {
  17. return format([target numberOfChips], @"Chip", @"Chips");
  18. },
  19. @"Tires" : ^(MyClass *target) {
  20. return format([target numberOfTires], @"Tire", @"Tires");
  21. }
  22. };
  23. }
  24. NSString *(^getTitle)(MyClass *target) = titleMapping[title];
  25. return getTitle ? getTitle(self) : nil;

Pro’s

1.非常安全,因为没有办法弄错if-else链. 2.缓存了映射,因为我们使用的是静态的变量. 3.我们可以很容易的添加一些宏来使得代码更精简,并且因此更容易来扩展.

PS.我可以用字符串匹配来实现它,甚至使用的代码更少. 但是我不认为它能使可读性更好.

更早使用 return 和取反 if 的判断条件来简化的流程

现在你大概可能发现我不太喜欢太多的if条件句并且我讨厌太长的if-esle链. 反而我比较喜欢让if条件语句尽可能的简单并且更早使用return来返回.

原始代码:

  1. if(!error) {
  2. //! success code
  3. } else {
  4. //! failure code
  5. }

我比较喜欢的写法:

  1. if(error) {
  2. //! failure code
  3. return;
  4. }
  5. //! success code

Pro’s 1.我不需要阅读更多的代码.假如我仅仅只是对error的情况感兴趣.

2.在大段代码的情况我不需要去记住所有的流程,因为我可以非常清楚看到前面的return/break.

 

使用动态方法解决

有时,我们可能看到类似下面的情况:

  1. if([type isEqualToString:@"videoWidget"]) {
  2. [self parseVideoWidget:dictionary];
  3. } else
  4. if([type isEqualToString:@"imageWidget"]) {
  5. [self parseImageWidget:dictionary];
  6. } else
  7. if([type isEqualToString:@"textWidget"]) {
  8. [self parseTextWidget:dictionary];
  9. } else
  10. if([type isEqualToString:@"twitterWidget"]) {
  11. [self parseTwitterWidget:dictionary];
  12. }

首先,我可以把上面讲的所有重构技巧应用到此,但是这段代码看起来主要等待解决的问题是将来的可扩展问题,让我们一起看看如何能够让它变得更好.

  1. SEL dynamicSelector = NSSelectorFromString([NSString stringWithFormat:@"parse%@:", type]);
  2. if(![self respondsToSelector:dynamicSelector]) {
  3. DDLogWarning(@"Unsupported widget type %@", type);
  4. return;
  5. }
  6. [self performSelector:dynamicSelector withObject:dictionary];

Pro’s 1.非常容易阅读并且理解 2.比if条件语句更安全 3.更容易扩展,例如我可以添加新的工具类型到分类中.当开发一个衍生APP的时候我甚至都不需要去接触它的基础类.

结论

我时常重构我的代码,这儿有一些知道人不多的技巧,但是有助于让你的代码更简化.我通常在“AppCode”上写我的代码.AppCode是一个很棒的IDE,它有着大量的关于重构的函数,我每天都在使用这些,点击链接下载.

时间: 2024-10-13 03:59:51

重构的技巧的相关文章

最实用的10个重构小技巧排行榜

LZ最近一直在研究虚拟机源码,可惜目前还只是稍微有一点点头绪,无法与各位分享,庞大的JAVA虚拟机源码果然不是一朝一夕能搞定的,LZ汗颜. 本次我们抛开JAVA虚拟机源码这些相对底层的东西,LZ来与各位探讨一下几个代码重构的小技巧,这些内容部分来自于书籍当中,部分来自于LZ维护项目当中的一些实践经验.如果猿友们曾经用过这种手法,也不妨参与到文章的留言当中,将你的小心得.小体会共享与他人,也可以拿来冲击LZ自己定义的排行榜,LZ不甚欢迎. 重构的手法有很多种,相对而言,一篇文章的涵盖量自然是无法提

最实用的代码重构小技巧排行榜TOP10

这次我们抛开JAVA虚拟机源码这些相对底层的东西,来与各位探讨一下几个代码重构的小技巧,这些内容部分来自于书籍当中,部分来自于项目当中的一些实践经验.如果猿友们曾经用过这种手法,也不妨参与到文章的留言当中,将你的小心得.小体会共享与他人,也可以拿来冲击LZ自己定义的排行榜,LZ不甚欢迎. 重构的手法有很多种,相对而言,一篇文章的涵盖量自然是无法提到所有,LZ这里也只能提出一些平时会经常使用的一些手法,像一些比较高端的手法,各位有兴趣的可以去找一些专门的书籍涉猎. 本文部分重构小技巧可能主要与JA

重构的技巧--cocoa china

我想一条童子军的军规:“始终保持露营地比你发现它的时候还要干净”.如果你在地上发现了一点脏东西,不管是谁弄的,都清理掉它.要为了下一拨来露营的人改善环境.(实际上,那条规矩的早期版本,出自Robert Stephenson Smyth Bden-Powell,童子军活动之父,说的是“努力使世界比你发现它时变得更好”.) 这就是为什么我不断的重构我的代码让它干净整洁. 当谈到编程的质量时,代码的可读性是我最关注的部分. 我想分享一些大部分人不知道的我用来简化代码的重构技巧. 字典的映射 假如你有一

页面重构css技巧总结篇(8.1-8.5)

1.如何让文字在容器内垂直居中? (1)方法:为容器添加line-height属性,使得line-height的值等于容器的height. (2)代码 <!doctype html> <html> <head> <meta charset="utf-8"> <title>无标题文档</title> <style type="text/css"> .container{ width:

代码重构的技巧——合理使用@Deprecated

最近由于工作环境不太理想,很长时间没心情写技术博文,今天在调试springMVC的DispatcherSevlet类的代码时,看到一处代码且联想到项目中程序员的一些做法,觉得有必要写一下. 我们在做项目时,前期写的类中的一些代码可能由于需求变化的原因需要重写,有时重写时方法的传参和返回值也会发生变化,这样往往导致调用的代码也跟着出错,我们看下spring的代码是如何处理这样的事,先看下面两段代码,上面这段是之前的getHandler方法,现在需要改成第二段代码,方法的参数cache不要了,而且方

重构培训有感

重构,对于一个工作刚刚满一年的我来说,本是一个陌生的概念.但好在,在参加本次培训之前,项目组内部已经开始对重构提起了重视,我也有幸读了<重构,改善代码的既有设计>这本书.其实,看过这本书的人可能都会有一个感觉,就是书中所概括的种种我们都知道,但是我们是否在意过这种细节,是否约束过自己,每个人心里都有一个答案吧.通过培训,还是对重构有了一个整体的认识. 我想,在重构之前,我们要明白为什么要对代码或者说软件进行重构.之所以要重构,是因为我们的软件中出现了这样或者那样的坏味道,具体的坏味道这里不做赘

重新组织函数--《重构》阅读笔记

1)寻找引用点时,最好使用工具,然后再人工review.在看到这个问题的时候,我估计应该是很久之前了.现在用IDE.这个要方便很多. 2)重新组织函数的方法和目标. 其实目标很简单.就是消灭长函数. 常用方法 Extract method Inline Method Replace Temp with Query Temporary Variable Replace Method with Method Object Remove Assignments to Parameters Substi

大学毕业4年-回顾和总结(6)-技术研发-重构之法

大部分的公司,开发项目都是作坊式的,没有产品和项目的需求分析,进而做出技术架构和详细设计. 很多人,听到上级和老板的一个想法,就开始写代码,边写边改,甚至推倒重来. 最终,导致的常见结果之一,项目代码混乱,新员工甚至老员工,对项目理解比较吃力. 如果你去改造代码,改好了,没有任何功劳.改出问题了,领导.测试.产品,很可能会说你不行. 这一点,是让很多程序员纠结的地方. 我个人还是倾向重构的,先熟悉项目总体环境,从易到难. 项目开发,从外部看,就是一个个的功能. 从内部看,不就是一个个函数和API

重构的艺术 深入认识

重构是什么? 重构就是对软件内部结构的一种调整,目的是不改变软件可观察行为的前提下,提高可理解性,降低修改成本.不是为了提高性能,是为了提高可读性,可维护性. 重构,从某种角度讲,就是整理代码.整理归类. 程序员,要戴着两顶帽子,一个是添加新功能,一个是重构.对完成功能的代码进行整理. 重构的目的? 改进设计,使软件更容易理解.找出Bug,提高后期的编程速度.代码使逐渐的腐败的,腐烂的.经常的重构就是保证软件保质期的方法. 何时重构? 事不过三,三则重构. 添加新功能时重构. 修补错误时重构.