`
san_yun
  • 浏览: 2600600 次
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

<clean code>学习笔记

阅读更多

 

 

有意义的命名

 

1.名副其实,体现本意。

int d; //时间,天计算

int elapsedTimeIndDays;

 

 

2.言简意赅,避免废话的命名

比如Product,还有一个ProductInfo或者ProductData。区别何在? 比如下面这个方法:

 

getActiveAccount();

getActiveAccountInfo();

程序员应该调用哪个方法?

 

 

3.类名应该是名词或者名称短语,如Customer,VikiPage,Account,AddressPaser。避免使用Manager,Processor,Data这样的类名,类名不应该是短语。

而方法名应该是动词或者动词短语,如postPayment,deletePage

 

4.一种概念统一用一种命名规则。

 

对于每个抽象概念选一个词,并且一以贯之。列入,使用get,load,query,find来给不同的DAO定义同样的方法。你怎么记得住那个类中哪个方法。你只能去看看详细看看方法原型,返回值是什么,注释是什么,然后才知道应该调用哪个方法。

 

方法

1.避免写过于复杂的函数。service的代码大部分都非常短小,但是我还是找到一个,下面这段截取自DefaultFormService的代码:

 

  public void init() throws ServiceInitializationException {

        initFormKeys();

 

        ResourceLoaderService resourceLoader = (ResourceLoaderService) getService(ResourceLoaderService.SERVICE_NAME);

 

        formConfig = new FormConfig(this);

 

        // 防止重复装入同一文件

        Set loadedDescriptors = new HashSet();

        StringBuffer rundataKey = new StringBuffer("form" + getInstanceName());

 

        for (Iterator i = getConfiguration().getList(FORM_DESCRIPTORS).iterator(); i.hasNext();) {

            String descriptor = (String) i.next();

            URL descriptorURL;

 

            try {

                descriptorURL = resourceLoader.getResourceAsURL(descriptor);

            } catch (ResourceNotFoundException e) {

                throw new ServiceInitializationException("Invalid URI broker descriptor: " + descriptor);

            }

 

            rundataKey.append(descriptorURL);

 

            if (!loadedDescriptors.contains(descriptorURL)) {

                loadDescriptor(descriptorURL);

                loadedDescriptors.add(descriptorURL);

            }

        }

 

        this.rundataKey = rundataKey.toString();

 

        // 处理group的继承关系

        processGroups();

 

        super.init();

}

也许你会花3分钟时间开懂这段代码,或者在一分半钟已经没有耐心看懂这段代码。这里面有太多的细节一下子展现在我们面前。有太多的不同层级的抽象。奇怪的字符串和函数调用。不过只要简单的方法抽离和重命名,就能让代码清晰很多,下面这段代码,好理解了吗?

重构之后:

public void init() throws ServiceInitializationException {

       initFormKeys();

       this.formConfig = new FormConfig(this);

       this.rundataKey = getRundataKey();

       // 处理group的继承关系

       processGroups();

       super.init();

    }

   

    private String getRundataKey(){

          ResourceLoaderService resourceLoader = (ResourceLoaderService) getService(ResourceLoaderService.SERVICE_NAME);

 

            // 防止重复装入同一文件

            Set loadedDescriptors = new HashSet();

            StringBuffer rundataKey = new StringBuffer("form" + getInstanceName());

 

           for (Iterator i = getConfiguration().getList(FORM_DESCRIPTORS).iterator(); i.hasNext();) {

                String descriptor = (String) i.next();

                URL descriptorURL;

 

                try {

                    descriptorURL = resourceLoader.getResourceAsURL(descriptor);

                } catch (ResourceNotFoundException e) {

                    throw new ServiceInitializationException("Invalid URI broker descriptor: " + descriptor);

                }

 

                rundataKey.append(descriptorURL);

 

                if (!loadedDescriptors.contains(descriptorURL)) {

                    loadDescriptor(descriptorURL);

                    loadedDescriptors.add(descriptorURL);

                }

            }

    }

    定义方法最基本的两条定律:第一规则短小,第二规则还是短小。代码应该短到多少合适?最多不能超过一个屏。最好能三行四行搞定。

 

2.函数内的代码应该保持同一抽象层次。

什么是抽象层次?原生的java API应该是最低抽象层次,比如

StringBuffer sb = StringBuffer(“form”); 

然后我们框架在上面封装属于中间抽象层次,比如:

  ResourceLoaderService resourceLoader = (ResourceLoaderService) getService(ResourceLoaderService.SERVICE_NAME);

最上面抽象层次是领域里面的业务方法。比如

initFormKeys();

之前的代码之所以复杂是因为不同抽象层次的代码混合在一起,导致在阅读代码的时候会有思维跳跃。代码层次越低需要了解的细节越多。我们读书看报都是先看看目录,提纲,最后再看感兴趣的章节,而不会一股脑的全出来所有的内容?代码也是如此。

3. 只做一件事情

         最前面的代码显然做了不只一件事情。函数应该只做一件事情,做好这一件事情。关键是那件事情是什么?代码只做了一件事情,对吧?其实也很容易看成五件事:

1.       初始化initFormKeys

2.       初始化FormConfig

3.       初始化getRundataKey

4.       处理group的继承关系。

5.       初始化父类。

如果函数做的那件事情其实应该是函数命名的那件事,比如postComplaint()应该做的事情就是发起投诉,发起投诉可能有很多步骤,第一步要检查参数是否有效。第二步要通知诚保,第三部要保存到数据库。如果函数只是做了该函数名下同一抽象层上的步骤,则函数还是只做了一件事情。

 

4.函数参数

1.标识flag参数。

标识参数抽离不堪,向函数中传入一个字符串判断等A做什么行为,等于B做什么行为?不如定义两个函数: doForA(),doForB();

 

2.如果函数有三个,或者三个以上的参数,就说明其中一些参数应该封装为类了。以前我对于这种做法非常粗暴,把所有参数都封装成一个类,这是非常不可取的。需要思考一下,把合适的参数封装到类里面。

有时候会纠结于函数的参数无法封装。比如投诉中方法A需要投诉方ID,被投诉方ID,交易单号,交易类型,投诉类型,而有的地方取不需要投诉类型,且投诉类型和交易信息不应该封装在一个VO里面。

 

3.无副作用

 

4.分割操作和询问

         函数要么做什么事情,要么回答什么事情,但二者不可兼得。看看这个例子:

public boolean setXXX(String attribute,String value);

应函数设置某个属性,如果成功就返回true。如果不存在这个属性就返回false,这样导致如下语句:

if(set(“username”,”unclebob”))..

 

好的方式是应该把操作和询问分开。

If(attributeExist(“username”)){

    setAttribute(“username”,”unclebob”);

}

 

6.       如何做到

Robert说他也无法做到一开始就按照这些规则写函数,一开始也是冗长而复杂,过长的参数列表,名称随意取的,也会有重复代码。但是他会配上一套单元测试,覆盖到每行丑陋的代码,最后打磨这些代码,分解函数,同时保持单元测试通过。

 

注释

1.       不要为了注释而注释。

2.       注释不能美化糟糕的代码。

3.       好的注释:

a)         法律信息

b)         提供信息

c)         对设计意图的解释

d)         警示

e)         TODO 注释

4.       坏的注释

a)         多余的注释

b)         注释掉的代码

c)         归属和署名(这个属于SVN源代码管理工具轻易提供的功能)

 

格式

 

1.       变量的定义和变量的使用不要隔的太远。

2.       相关函数放在一起,若某个函数调用了另外一个,就应该把他们放在一起。而且调用者应该尽可能的放在被调用者的上面。这样程序就有了一个自然的顺序。若坚定地遵守这条约定,读者就能够确信函数声明再起调用后很快出现。

 

数据结构

 

异常处理

 

单元测试

 

 

分享到:
评论

相关推荐

    install、clean、add addtional source工程和maven仓库8.1

    &lt;plugin&gt; &lt;groupId&gt;org.codehaus.mojo&lt;/groupId&gt; &lt;artifactId&gt;build-helper-maven-plugin&lt;/artifactId&gt; &lt;version&gt;1.5&lt;/version&gt; &lt;executions&gt; &lt;execution&gt; &lt;id&gt;add-source&lt;/id&gt; ...

    TS reader

    ait descriptors parsing&lt;br/&gt;&lt;br/&gt;2003.01.19:&lt;br/&gt; - fixed bug for ait section parsing&lt;br/&gt; - started code clean up and consolidation for easy porting&lt;br/&gt;&lt;br/&gt;2003.01.13:&lt;br/&gt; - added initial ait ...

    Test Driven Development

    The goal is clean code that&lt;br/&gt;works, and for a whole bunch of reasons:&lt;br/&gt;• Clean code that works is a predictable way to develop. You know when you are&lt;br/&gt;finished, without having to worry ...

    使用Maven管理进行多模块开发案例

    &lt;/modules&gt; packaging节点只能指定为pom,modules节点说明由几个模块组合,上面是把我们经常使用的架构分层模式分成一个个组件进行开发dao-&gt;service-&gt;web层。此pom文档经常还被用来进行一些依赖管理和插件管理,...

    NutzBoot的maven插件

    &lt;groupId&gt;org.nutz.boot&lt;/groupId&gt; &lt;artifactId&gt;nutzboot-maven-plugin&lt;/artifactId&gt; &lt;version&gt;${nutzboot.version}&lt;/version&gt; &lt;/plugin&gt; &lt;/plugins&gt; &lt;/build&gt; 打包jar文件 mvn clean package nutzboot:shade ...

    基于ssm+shiro+redis+nginx tomcat服务器集群管理项目源码+项目说明.zip

    mvn clean install -Dmaven.test.skip=true &lt;br/&gt; ssm-parent&lt;br/&gt; 父模块,其他模块会继承该模块,引入公共的依赖&lt;br/&gt; ssm-model&lt;br/&gt; 模型层模块,提供各种POJO。包括与数据库表对应的模型、传输模型等。提供给...

    IDQ漏洞溢出源代码

    从Internet上学习到的,也让他回归internet.&lt;br&gt;&lt;br&gt; &lt;br&gt;&lt;br&gt;文件结构:&lt;br&gt;&lt;br&gt;cpp文件: iisidqoverflow.cpp 和 SkShellCodeFunc.cpp &lt;br&gt;头文件: SkShellCodeFunc.h &lt;br&gt;功能文件: WSAStart.cpp和SnakeSocket.cpp...

    BUS Hound

    extended scan code E0&lt;br&gt; 0004h = extended scan code E1&lt;br&gt;6 2 Not used&lt;br&gt;8 4 Device specific information&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;The Save Window&lt;br&gt;&lt;br&gt;The Save button saves all captured data to the ...

    日志监测 实时监测

    &lt;name&gt;Clean Logs&lt;/name&gt; &lt;triggers&gt; &lt;!-- run once per day. ForceBuild is required if there is no source code change --&gt; &lt;intervalTrigger seconds="86400" buildCondition="ForceBuild" /&gt; &lt;/...

    解决struts2.1.6+spring+hibernate 中文乱码

    &lt;filter-name&gt;struts-cleanup&lt;/filter-name&gt; &lt;filter-class&gt; org.apache.struts2.dispatcher.ActionContextCleanUp &lt;/filter-class&gt; &lt;/filter&gt; &lt;filter-mapping&gt; &lt;filter-name&gt;struts-cleanup&lt;/filter-name&gt; ...

    用Java的制作一个QQ机器人

    ... 根据文档将go-cqhttp的上报方式修改为Array 克隆并执行mvn clean install 创建你的 springboot 项目并引用依赖 &lt;dependency&gt; ... &lt;version&gt;1.0.0&lt;/version&gt; &lt;/dependency&gt; 在启动类上加注解@EnableBot

    Write Clean Code Write Clean Code Write Clean Code Write Clean Code

    微软书籍Write Clean Code 微软书籍Write Clean Code 微软书籍Write Clean Code 微软书籍Write Clean Code

    Struts2整合SiteMesh技巧

    &lt;filter-name&gt;struts-cleanup&lt;/filter-name&gt; &lt;filter-class&gt;org.apache.struts2.dispatcher.ActionContextCleanUp&lt;/filter-class&gt; filter&gt; &lt;filter&gt; &lt;filter-name&gt;sitemesh&lt;/filter-name&gt; &lt;filter-class&gt;...

    Writing Clean Code(DOC)

    现在,Microsoft已经比我刚进公司时大了九倍。很难设想,倘若没有准确的指南,公司怎样才能将出错率降低到原来的水平。尤其是在Windows和Macintosh的应用越来越复杂的情况下...&lt;br&gt;&lt;br&gt;Steve Maguire&lt;br&gt;西雅图,华盛顿

    蓝牙sco连接源码

    ./blue&lt;br&gt;3) put your device in \'discovery mode\',&lt;br&gt;4) select \'search\' on textual menu,&lt;br&gt;5) select your device to connect with,&lt;br&gt;First time you are requested to enter pin code.&lt;br&gt;6) select ...

    Web快速开发框架NanoFramework.zip

    mvn clean install -Dmaven.test.skip=true依赖&lt;dependency&gt;  &lt;groupId&gt;org.nanoframework&lt;/groupId&gt;  &lt;artifactId&gt;nano-server&lt;/artifactId&gt;  &lt;version&gt;1.3.13&lt;/version&gt; &lt;/dependency&gt; ...快速启动示例...

    Ruby Pocket Reference

    &lt;br&gt;By Michael Fitzgerald&lt;br&gt;First Edition July 2007 &lt;br&gt;Pages: 176 &lt;br&gt;Series: Pocket References&lt;br&gt;ISBN 10: 0-596-51481-6 | ISBN 13: 9780596514815&lt;br&gt;&lt;br&gt;出版社:O’Reilly&lt;br&gt;&lt;br&gt;...

    mtk软件编译过程

    编译工具和辅助工具:&lt;br&gt;ADS1.2&lt;br&gt;ADS12_update_842.exe&lt;br&gt;MSYS-1.0.10.exe&lt;br&gt;MinGW-3.1.0-1.exe&lt;br&gt;ImageMagick-6.2.5-5-Q16-...&lt;br&gt;make custom=proj gprs new&lt;br&gt;其中,命令可以为 clean, update, remak&lt;br&gt;

    编程精粹 --- 编写无错的c程序

    &lt;br/&gt;&lt;br/&gt;编写优化、高效、无错地代码 &lt;br/&gt;&lt;br/&gt;编程精粹&lt;br/&gt;─── Microsoft 编写优质无错C 程序秘诀&lt;br/&gt;&lt;br/&gt;Writing Clean Code&lt;br/&gt;─── Microsoft Techniques for Developing Bug-free C Programs&lt;br/&gt;...

    c++程序代码

    /// &lt;summary&gt; /// Required designer variable. /// &lt;/summary&gt; private System.ComponentModel.IContainer components = null; /// &lt;summary&gt; /// Clean up any resources being used. /// &lt;/summary&gt; //...

Global site tag (gtag.js) - Google Analytics