使用更有意义的命名
在大部分编程活动中我们都是在给各种各样的元素来命名如果你取了一个好名字不仅能让元素的职责马上清晰起来而且能使代码更好维护。在命名的时候注意尽量使用声明方式的词语不要用实现来命名比如GetUser显然比GetUserFromDataBase要好。如果你存在一个GetUserFromDataBase方法很有可能后面会出现一个GetUserFromFile方法。我想你应该能明白我的意思了吧GetUserFormDataBase和GetUserFromFile更应该是两个兄弟类中的对等方法这时迈向面向对象的第一步。命名还有另外一个层次的意义给一段表达式命名。如果一段表达式很难理解你甚至要看好几眼甚至要写一行注释来说明那么我们为什么不将这个表达式提取为一个方法呢然后给这个方法一个非常好的命名呢提取方法之后不仅代码更好理解了而且这段表达式的抽象层次也就相应地提高了。而且如果一个方法内部各个部分理解的难易程度居然不一致那么很可能是该方法内部各部分之间抽象层次不一致有的地方太过于细节有的地方又只覆盖大的方面。在《Clean Code》里Uncle Bob告诉我们在同一级别的元素中抽象的层次应该是一样的。比如下面这段代码1: String defaultSignature journalService.getDefaultSignature(journal);2: if(StringUtils.isBlank(defaultSignature) journal.hasEditor()){3: return defaultSignature journal.getEditor().getName();4: }在上面的代码中就存在抽象层次不一致的地方journal.hasEditor()抽象层次比StringUtils.isBlank(defaultSignature)和下面的那个拼接字符串的层次都要高。那么我们可以将上面的代码改写为下面这个样子1: if(hasDefaultSignature(journal) journal.hasEditor()){2: return getEditorSignature(journal);3: }4:5: private boolean hasDefaultSignature(Journal journal){6: String defaultSignature journalService.getDefaultSignature(journal);7: return StringUtils.isNotBlank(defaultSignature);8: }9:10: private String getEditorSignature(Journal journal){11: return journalService.getDefaultSignature(journal);12: }方法数目变多了但是我们现在甚至可以直接从方法的名字就可以读出代码的逻辑了。可能有的同学会发现这里有个重复的调用这个会在后面提及。命名还有个要注意的是不要在名称中使用and/or等字眼如果你的方法或者类名居然要使用这样的字眼那么很明显你违反了单一职责原则。更小的类更小的方法如果说命名还是不好操作什么样的名字算好什么样的算不好什么样子的是抽象层次不一致。那么现在的这条原则就好操作得多了。我们可以在团队里实行这样的原则类不要超过150行方法不要超过12行额为什么是150和12为什么不是160和15不好意思我这是随手敲出来的争论具体的数字是没有意义的我想说的是使用一个可量化的标准定下来并且获得团队内部所有成员的认可和遵守。每当你看到一个庞大的类或这方法时你就尝试通过不断地提取新类将干的事儿差不多的方法提到新的类中提取新的方法通过上面一条说的方式将类和方法的规模不断地缩小。小的类和方法不仅非常容易读懂而且重用的机会也就更大。小类和小方法有很多好处你要尝试之后才知道真的~但是下面这一条却更为重要也是走向面向对象设计的必由之路。消除重复代码逻辑随着项目地进行我们的代码库不断地增长必定会存在一些重复的地方。相同的代码相同的逻辑充斥在各个地方。重复的代码大部分时候是因为CopyPaste造成的大部分人看看这两个地方实现相同然后复制过来稍做修改就宣告完成。但这也是引入bug的最佳时机如果一个地方错了那么处处出错而且你也丧失了一次进行面向对象的机会。我们不能容忍重复的代码如果遇到两块重复的代码我们就要想办法提取方法复用之提取”辅助“类复用之提取基类继承之。但是重复的的代码也有可能不是那么显而易见还有可能是部分重复部分不重复。这个时候我们就要借助一些重构的手段比如我们可以将以前一些提取出去的方法内联进来将调用顺序修改修改。突然我们发现重复代码一致了这部分内容可以参见Martin Fowler的《重构》。一行代码只出现一个点其实这个原则有个更学术的名字迪米特法则The Law of Demeter。意思就是只和身边的朋友交流你不要通过点点点获取一个很远的对象的状态然后再来做些计算。这样打探别人的隐私是不好的你应该思考一下你为什么要这样做是不是现在这段代码放错了地方也就是你越权了。比如下面这段代码1: rover.Position.X 1;2: rover.Position.Y 3;还好这里只有两个点但是你有没有想过我为什么要通过这种方式获取rover的位置然后给它的x坐标加1来移动rover呢你为什么不采用一种更好的方式1: rover.Move(1,3);让rover自己移动好了至于内部的实现我不想知道我不想知道你用的是直角坐标系还是大地坐标系。那么你现在有没有那么一点感觉你的代码更面向对象了呢我觉得我是有的。你太着迷这个类了你写了一段代码你惊奇的发现这段代码居然没有使用自己所在类的任何属性然后却不断地打探另外一个类的内部属性然后做些操作。这个时候你是不是要考虑一下这段代码不应该放在这儿吧。就如本文第一段代码所示这段代码不断地去打探Journal对象内部的东西。但是对自己类的东西却一点都不感兴趣好吧我觉得我们应该将这段代码移动到Journal里面去变成一个getSignature方法。不要getter/setter你有没有发现过你有很多类只是一堆的getter/setter其余的啥东西都没有。所以这个时候我们的”代码生成器“有用武之地了我当然不是反对使用代码生成器。很多人都是想都不想就将getter/setter全部生成出来而.NET的自动实现属性更简化了这一操作让对象”随意地“暴露自己的内部状态变得更流行。如果你暴露了许多内部状态后面的代码就很容易打探到对象内部状态也就更容易将本应该属于这个对象的职责的代码写到别的类中如果你没有暴露任何内部状态我要干一件事儿的时候我不能问你了我只能请求你帮我完成然后你这个对象就丰富起来了。这就是传说的”吩咐不要询问“的原则。关于随意地添加getter/setter在《OOD启思录》这本书里有更好的讨论另外这真是一本好书。后记可操作性强的实践还有很多这里只是举几个例子。后面我会给出一些参考书籍。当然因为要可操作性强有的时候可能会太绝对我想什么东西都是权衡的结果。还是那句话如果你觉得你对面向对象已经驾轻就熟那么是没有什么必要记住这些东西了但是如果你像我一样头脑简单难以理解那些抽象的论述而且对小猫小狗的示例也不感兴趣那么你大可一试我想应该是没有什么坏处的。参考书籍