《代码整洁之道》总结与演绎(上)

版权声明:本文为博主原创文章,未经博主允许不得转载,如有问题,欢迎指证。 https://blog.csdn.net/weixin_39076229/article/details/89713456

程序员可以分为三个层次:普通程序员、工程师 、架构师

有意义的命名

1、名副其实

(1)变量、函数或者类的名称应该已经答复了所有问题,应当说明了做什么事,应该怎么用

(2)如果名称需要注释来补充,就不算名副其实。

(3)以日期为例:应当指明计量对象和计量单位

int d ------> int daysSinceCreation / int daysSinceModification / int fileAgeInDays

2、避免误导

(1)别用accountList来指一组账号,除非真的是List类型

accountList -----> accountGroup / bunchOfAccounts

(2)提防使用不同之处较小的名称,即外形非常相似,只有微小处的区别

XYZControllerForEfficientHandlingOfStrings ----- XYZControllerForEfficientStorageOfStrings

(3)不要以 l 和 o 做变量名,难以和 1 , 0 区分

3、做有意的区分

(1)添加数字或废话(info、data)是错误的

int a1, int a2 -----> int source , int destination

accountData 与 account没有区别 , theMessage 与 message 没有区别

(2)Variable一词永远不应当出现在变量名中,Table一词永远不应当出现在表名中

4、使用读得出来的名称

(1)切记不要傻乎乎自造词,而使用英语词

generationTimestamp    modificationTimestamp    recordId

5、使用可搜索的名称

(1)名称长短应该与其作用域的大小相对应,若变量或者常量可能在代码中多处使用,应该赋予其便于搜索的名称。

6、避免使用编码

(1)不要把类型或作用域以前缀或后缀方式编进名称

IShapeFactory , ShapeFactory ------> ShapeFactroy , ShapeFactoryImpl

7、避免思维映射

(1)在多数情况下,单字母名称不是个好选择

8、类名

(1)类名和对象名应该是名词或者名词短语

Customer、WikiPage、Account这些名词,避免使用Manager、Processor、Data或者Info这样的类名。类名不应当是动词

9、方法名

(1)方法名应该是动词或者动词短语

postPayment、deletePage或者save

(2)属性访问器、修改器和断言应该根据其值命名,依据Javabean标准加上get、set、is前缀

10、每个概念对应一个词

(1)给每个抽象概念选一个词,并且一以贯之

使用fetch、retrieve和get来给在多个类中的同种方法命名。

11、别用双关语

(1)遵循“一词一义”,避免将一个单词用于不同的目的

(2)代码作者应尽力写出易于理解的代码

12、词汇领域

(1)尽量用计算机领域词汇命名,如果不能,用所涉领域名称命名

17、不要添加没用的语境

(1)只要短名足够清楚,就要比长名称好

例如需要将邮件地址、MAC地址和web地址相区别:PostalAddress、MAC 和 URL

函数

1、短小

(1)函数的第一规则是要短小,函数20行封顶最佳;

(2)每个函数都只做一件事,每个函数依序把你带到下一个函数;

2、只做一件事

(1)函数中的语句都要在同一抽象层级;

(2)让代码拥有自顶向下的阅读顺序;

3、switch语句

(1)switch语句违反了单一职责原则(Single Responsibility Principle)、开放封闭原则(Open Closed Principle),可以用工厂模式进行重构。

  • 把case中的角色抽象成继承(实现)了统一抽象类(接口)的子类;
  • 子类各自实现各自的抽象方法;
  • 工厂进行定制,按需创建子类的对象
public class BeforeRefraction {

     enum EmployeeType{
        ENGINEER,SALESMAN,MANAGER;
     }
     public static int m_basicSalary;
     public static int m_commission;
     /**
     * switch语句接收枚举类型,判断输出相应的薪资算法
     * @param empType    Employee type in enum
     * @return           Payment of each type
     */
     public int PayAmount(EmployeeType empType) throws Exception{
         switch (empType){
             case ENGINEER:
                 return m_basicSalary;
             case SALESMAN:
                 return m_basicSalary + m_commission;
             case MANAGER:
                 return 2 * m_basicSalary;
             default:
                 throw new Exception("no such employee type!");
         }
     }
    /**
     * switch再来一遍,接收枚举类型,判断输出相应的职位描述
     * @param empType    Employee type in enum
     * @return           Description of each type
     */
     public String GetDescription(EmployeeType empType) throws Exception{
         switch (empType){
             case ENGINEER:
                 return "Coding, Debug, Optimization";
             case SALESMAN:
                 return "Getting contracts";
             case MANAGER:
                 return "Analysis, Scheduling, Reporting";
             default:
                 throw new Exception("no such employee type!");
         }
    }
}
//----------------------------------------------------------------------------------
// Step1:把Switch语句中的每个case抽取出来:
//    class Engineer
//    class Salesman
//    class Manager
// Step2:把它们共有的部分抽象为一个抽象类或者接口: abstract class Employee
// Step3:工厂接收抽象类或者接口的引用变量,再根据引用变量的类型按条件new出具体的子类。工厂可以是一个单独的类,也可以是放在上面的抽象类中

enum EmployeeType{
    ENGINEER,SALESMAN,MANAGER;
 }

abstract class Employee{
    public static int m_basicSalary;
    public static int m_commission;
    /*
    * 工厂方法也可以放在这里,仍然是一个静态方法
    */
    public abstract String getDescription();
    public abstract int PayAmount();
}

//把Switch语句中的每个case抽取出来,然后把他们共有的部分抽象为一个抽象类或者
class Engineer extends Employee{
    @Override
    public String getDescription() {
        return "Coding, Debug, Optimization";
    }

    @Override
    public int PayAmount() {
        return  m_basicSalary;
    }
}

class Salesman extends Employee{
    @Override
    public String getDescription() {
        return "Getting contracts";
    }

    @Override
    public int PayAmount() {
        return  m_basicSalary + m_commission;
    }
}

class Manager extends Employee{
    @Override
    public String getDescription() {
        return "Analysis, Scheduling, Reporting";
    }

    @Override
    public int PayAmount() {
        return  m_basicSalary *2;
    }
}

/**
* 工厂类,这里只有一个静态方法
* 工厂类只做一件事:按条件new对象
* 这样一来,我们在main方法中就不用new不同的employee对象了
*/
class Factory{
    public static Employee getEmployee(EmployeeType employee) throws Exception{
        switch(employee){
            case ENGINEER:
                return new Engineer();
            case SALESMAN:
                return new Salesman();
            case MANAGER:
                return new Manager();
            default:
                throw new Exception("no such employee type!");
        }
    }
}
public class AfterRefraction {
    public static void main(String[] args) {
        Employee employee = null;
        try {
            employee = Factory.getEmployee(EmployeeType.ENGINEER);
        } catch (Exception e) {
            e.printStackTrace();
        }
        employee.getDescription();
        System.out.println("Salary:"+employee.PayAmount());
    }
}

//注:上述代码案例取自于Sarah_mq博客(原文:https://blog.csdn.net/weixin_35813749/article/details/53837878)

4、使用描述性的名称

(1)函数越短小、功能越集中,就越便于取个好名字;

(2)长而具有描述性的名称,要比短而令人费解的名称好,尽量名称能说清其功用;

(3)命名方式要保持一致,使用与模块名一脉相承的短语、名词和动词给函数命名。

includeSetupAndTeardownPages , includeSetupPages , includeSuiteSetupPage , includeSetupPage等

5、函数参数

(1)最理想的参数数量是0,其次是1,再次是2,应该尽量避免3;

(2)减少参数数量,便于覆盖所有可能进行测试

(3)如果函数要对输入参数进行转换操作,转换结果就该体现为返回值;

StringBuffer transform(StringBuffer in) 要好于 void transform(StringBuffer in)

(4)标识参数:向函数传入布尔值是不好的做法,意味着本函数不止做一件事。

(5)参数对象:如果函数需要2个、3个或3个以上参数,就说明其中一些参数应该封装为类了;

Circle makeCircle( double x , double y , double radius) ----> Circle makeCircle(Point center , double radius)

(6)参数列表:有可变参数的函可能是一元、二元甚至是三元,超过这个数量就可能要犯错了。

void monad(Integer ... args);

void dyad(String name , Integer ... args);

void triad(String name , int count , Integer ... args);

(7)动词与关键字

write(name) ----> writeField(name):告诉读者“name”是一个“field”

assertEqual -----> assertExpectedEqualsActual(expected , actual)

(8)分隔指令与询问:函数要么做什么事,要么回答什么事,但二者不可兼得

6、使用异常替代返回错误码,错误处理代码能从主路径代码中分离出来

(1)抽离Try/Catch代码块:另外形成函数处理正常流程

public void delete(Page page){
    try{
        deletePageAndAllReferences(page);
    }catch(Exception e){
        logEeeor(e);
    }
}
private void deletePageAndAllReferences(Page page) throws Exception{
     deletePage(page);
    registry.deleteReference(page.name);
    configKeys.deleteKey(page.name.makeKey());
}
private void logError(Exception e){
    logger.log(e.getMessage());
}
//上述delete函数只与错误处理相关,很容易理解然后忽略掉;
//deletePageAndAllReference函数只与完全删除一个Page有关;
//错误处理可以忽略掉

(2)错误处理就是一件事:catch/finally代码块后面也不该有其他内容

(3)Error.java依赖磁铁:其他许多类都得导入和使用它,当Error枚举修改时,所有其他的类都需要重新编译和部署,对Error类的修改造成了负面的压力,而使用异常代替错误码,新异常就可以从异常类派生出来,无需重新编译或重新部署。

7、别重复自己

(1)代码因此而臃肿,且当算法改变时需要修改所有的重复代码块,重复是软件中一切邪恶的根源。

(2)打磨代码:分解函数、修改名称、消除重复

注释

1、注释不能美化糟糕的代码

(1)注释存在的越久,就离其所描述的代码越远,越来越变得全然错误,因为程序员不能坚持维护注释;

(2)写注释的常见动机之一是糟糕代码的存在;

2、用代码来阐述

(1)用代码解释大部分的意图

3、好注释

(1)唯一真正好的注释是你想办法不去写的注释

(2)版权及著作权声明

(3)注释吧某些晦涩难明的参数或返回值的意义翻译为某种可读形式

(4)用于警告其他程序员会出现某种后果的注释也是有用的

// SimpleDateFormate is not thread safe, so we need to create each instance independently

SimpleDateFormat df = new SimpleDateFormat("EEE, dd MMM HH:mm:ss z");

(5)TODO注释,放置要做的工作列表

(6)注释可以用来放大某种看来不合理之物的重要性

(7)如果编写公共API,就该为它编写良好的Javadoc

4、坏注释

(1)不要对显然之事喋喋不休,毫无新意

private void startSending(){
    try{
        doSending();
    }catch(SocketException e){
        // normal:someone stopped the request
    }catch(Exception e){
        try{
            response.add(ErrorResponder.makeExceptionString(e));
            response.closeAll();
        }catch(Exception e){
            // Give me a break
        }
    }
}
//对于最末一个try/catch代码块拆解到单独的函数中
private void startSending(){
    try{
        doSending();
    }catch(SocketException e){
        // normal:someone stopped the request
    }catch(Exception e){
      addExceptionAndCloseResponse(e);   
    }
}

private void addExceptionAndCloseResponse(Exception e){
    try{
         response.add(ErrorResponder.makeExceptionString(e));
         response.closeAll();
       }catch(Exception e){
            
       }
}

(2)直接把代码注释掉是一种令人讨厌的做法

(3)如果一定要写注释,请确保它描述了离它最近的代码,别在本地注释的上下文环境中给出系统级的信息。

(4)信息过多:别在注释中添加有趣的历史性话题或者无关的细节描述

(5)短函数不需要太多描述,只需要为短函数选个好名字

------------------------------------------------------------------ end ---------------------------------------------------------------------------------------------------

欢迎关注微信公众号“JAVA万维猿圈”,记录程序员修炼之路,共同学习和成长!

猜你喜欢

转载自blog.csdn.net/weixin_39076229/article/details/89713456