《代码整洁之道》Clean Code第三章读书笔记

第三章 函数

  1. 自顶向下阅读代码

  2. 如果单纯使用switch语句,每次增加新case时,需要在原来代码的基础上增加新的case。这违反了单一权责原则(因为有很多修改它的理由),以及开放闭合原则(每当添加新类型时,就必须修改原来的函数)。

    1. 解决方法是将switch语句埋藏到抽象工厂底下(由下面的代码片段一变成再下面的代码片段二)。

      public Money calculatePay(Employee e) throws InvalidEmployeeType {
          switch (e.type) {
              case COMMISSIONED:
                  return calculateCommissionedPay(e);
              case HOURLY:
                  return calculateHourlyPay(e);
              case SALARIED:
                  return calculateSalariedPay(e);
              default:
                  throw new InvalidEmployeeType(e.type);
          }
      }
      
      
      
      public abstract class Employee {
          public abstract boolean isPayday();
          public abstract Money calculatePay();
          public abstract void deliverPay(Money pat);
      }
      
      public interface EmployeeFactory {
          public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType;
      }
      
      public class EmployeeFactoryImpl implements EmployeeFactory {
          public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType {
              switch (r.type) {
                  case COMMISSIONED:	
                      return new CommissionedEmployee(r);
                  case HOURLY:
                      return new HourlyEmployee(r);
                  case SALARIED:
                      retuen new SalariedEmployee(r);
                  default:
                      throw new InvalidEmployeeType(r.type);
              }
          }
      }
      
      
      
      
  3. 别害怕长名称,局域描述性的长名称,要比短而令人费解的名称好。使用某种命名约定,让函数名称中的多个单词容易阅读,然后使用这些单词给函数起个能说明其功能的名称。

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

  5. 函数参数(输入参数)。

    1. 最理想的参数数量是0,其次是1,再就是2,应尽量避免3个以上的参数。

    2. 向函数传入某个参数有两种普遍的类型。

      1. 问关于参数的问题,eg boolean fileExists("MyFile")

      2. 操作该参数,把其转换为某种东西再输出eg InputStream fileOpen("MyFile")

      3. 事件,在这种形式中,有输入参数而无输出参数。程序将函数看做一个事件,使用该参数能修改系统状态。eg void passwordAttempFailedNtimes(int attempts),请小心使用这种形式,应该让读者了解这是一个事件,并谨慎的选用名称和上下文。

      4. 不推荐向函数传输boolen类型的输入参数,因为这样做,方法签名会变得复杂,且相当于大声宣布此函数不只是做一件事情,即为true时做一件事情,为false时做另外一件事情。可以将该函数拆分为两个无参函数,在参数为true时的无参函数起一个名字,为false时无参函数起另一个名字。

      5. 有的时候双函数刚刚好(比如标示一个坐标),而有的时候应该尽量将双函数拆分为单参数函数。

      6. 如果有些函数需要2个、3个或者3个以上的参数,就说明其中一些参数该封装为类了。

        Circle makeCirle(double x, double y, double radius);
        Circle makeCirle(Point center, double radius)
        
      7. 动词与关键词。给函数起个好名字,能较好的解释函数的意图,以及参数的顺序和意图。对于单参数函数,应该形成一种非常良好的动词/名词对形式。例如,write(name),更好的命名应该是writeFiled(name),这告诉我们,name是一个filed。或者也可以把参数的名称编码为函数名,例如把assertEquals改为assertExpectedEqualsActual(expected, equals),这用便可大大减轻记忆参数顺序的负担。

      8. 函数参数(输出参数)。普遍而言,应该避免使用输出参数。输出参数是指:一个函数的输入参数,你修改了它的内部状态,但是却啥也不提示。eg appendFooter(s),只看这个调用可能会比较迷惑,当看了函数的全部签名后eg public void appendFooter(StringBuffer report)后,才能明白。在面向对象编程出现之前,可以接受输出参数,但是在面向对象之后,对输出参数的需求就已经消失了,因为this也有输出函数的意味。换言之,你可以使用report.appendFooter()

  6. 分隔指令与询问。函数要么做一件事情,要么问一件事情,但是二者不可兼得。函数应该修改某对象的状态,或者返回该对象的有关信息。如果两样都干,就会导致混乱。例如下面的函数,它会判断属性是否存在之后,再为其设置value。

    public boolean set(String attribute, String value)
    

    更好的做法是,将其拆分为两个函数

    if (attributeExists("UserName"))
        setAttribute("UserName", "unclebob");
    
  7. 使用异常替代返回错误码。从指令式函数返回错误码略微违反了指令与询问分隔的规则,因为它鼓励了在if语句判断中把指令当做表达式使用,虽然其不会引起动词、形容词混淆,但是会导致更深层次的嵌套结构。当返回错误码时,它要求调用者立刻处理错误。但是如果使用异常替代错误码,错误处理代码就能从主路径中分离出来,从而得到简化。

  8. 抽离try/catch代码块。

  9. 一般错误码会在一个类或者枚举中定义,但是这样的类就是一个依赖磁铁。其它很多类都得导入和使用它,当这个类重新编译时,所有其它的类都需要重新编译和构建。使用异常代替错误码,新异常就可以从异常类中派生出来,从而无需编译或者重新部署。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值