重构心法修炼第四层:简化函数调用

10.1  函数改名

        当函数的名称未能够揭函数的用途,那么就修改函数的名称。

        抽取出来的方法虽然可重用性与可复写性变高,但需要将它们的起一个好名字。否则我将会需要费劲周折来弄清楚这些函数的功能。有一个比较好的办法,先为这个函数增加一个注释,再使用用注释成为方法名。

10.4  将查询函数与修改函数分离

        某个函数即返回了对象的状态值,也修改了对象的状态。那么建立两个不同的函数,其中一个负责查询,另外一个负责修改。

        如果一个函数即通过一定的查询条件返回了一个值,又在这个函数内修改了某个外部的对象。可以将查询独立为一个函数,再将修改状态值独立为一个函数。将修改对象的状态的值抽取为函数,往往会复用到新独立的查询函数。

        if(  found(personList) != null  )  {  sendMessage();  }     

       以前sendMessage()方法在found()方法内部,现在独立。

10.5  令函数携带参数

        若干函数做了类似的工作,但是在函数本体中却包含了不同的值。那么就建立单一函数,以参数表达那些不同的值。

        如果有多个方法做了类似的事情,但其中有几个地方可能有所不同。可讲这些不同的地方抽取为一个参数,并将它们其它相同的部分合并为一个方法。

        void raise(double factor) {  salary = salary * factory; }

10.6  以明确的函数取代参数

        有一个函数,其中完全取决于参数值而采取不同的行为。针对该参数的每一个可能的值,建立一个独立函数。

        一种值对应的是一种行为,那么就可以明确的将这种行为抽取为一个独立的方法。

        在以前调用处,例如 EmployFactory.getEmployeeByType(Employee.MANAGER)直接替换为静态方法getManager()。这样就避免了工厂判断参数是否合法的麻烦。

10.7  保持对象完整性

        如果从某个对象中取出若干的值,将这些值作为参数传递。不如改为传递整个对象。

        此重构的优点在于提高代码可读性,并且有效的缩短了函数的参数列表。另外一个好处在于,如果不是传递的是对象,万一以后要用到这个对象的另外一个值,之前用了这个对象的那么多值,说不定就会添加一个新的。这个时候,只能尴尬的查找并修改此函数的所有引用。而传递整个对象就有效的避免了此尴尬的发生。

        但是事物都有两面性,如果确实函数参数很长,那就传递整个对象。但是代价在于函数与参数对象耦合,可能会导致一些结构上的恶化。

10.8  以函数取代参数

        对象调用某个函数,并将所得结果作为参数,传递给另外一个函数。而接收该参数的函数本身也能够调用前一个函数。让参数接收者取出该参数,并直接调用前一个函数。

        int basePrice =  quantity *  price ; //在方法里面直接调用且不是函数参数,说明就是方法的成员变量

        discountPrice(  getDiscountLevel()  ,basePrice);

重构后

        //因为 quantity *  price 是可以直接调用的,至少在这个类中可以自由调用,那么将其提炼为 getBasePrice()方法

        //并去除 discountPrice( int level ,  basePrice) 的 basePrice参数

        discountPrice(  getDiscountLevel()  );

10.9  引入参数对象

        某些参数总是很自然的同时出现。可以以一个对象取代这些参数。

        成对出现的参数可用对象封装。可以有效的缩短参数列表。

10.10  移除设值函数

        类中的某个字段应该在对象创建的时候被设置,然后就不再改变。去掉该字段的SET方法。

        类中某个变量只希望在创建的时候被设置,然后就不希望被修改。可以使用构造函数接收此值,并用final修饰此字段,并去掉该变量的SET方法。

     public  JsonReponseHelper  ......

                 public final JsonReponseVO  vo  = new JsonResponse();

                 JsonReponseHelper (String reason){  vo.setSuccess(false); vo.setReason(reason);}

     //不提供VO的SET方法,并在用public 与 final 修饰符 公开目的,可以自由调用,但是只能在初始化的时候设置一次。

10.12  以工厂取代构造函数

        工厂类的使用目的之一就是向客户端隐藏实现。

        通过工厂类提供一个抽象类或者接口的实现类,并且向上转型成为一个接口的形式。那么就可以有效的隐藏了我们不想公开的类。

10.13  封装向下转型

        某个函数返回的对象,需要由函数调用者执行向下转型的操作。将向下转型的动作移到函数中。

        帮助客户端完成向下转型。

10.14  以异常取代错误码

        某个函数返回了一个特定的代码,用以表示某种错误的情况。改用异常。

        返回一个自定义异常,一般继承自Exception或者RuntimeException,因为它们提供了包裹异常的构造器。所以自定义异常可以携带错误信息和堆栈信息给上层调用者。

10.15  以测试取代异常

         面对一个调用者可以预先检查的条件,你抛出了一个异常。修改调用者,使它在调用函数之前先做检查。

        可以明确预见到可能会发生的异常,可以使用前置条件来做检查,而不是滥用异常。例如数组角标越界以及空指针异常。

        异常可以表示一些未知的错误,例如在解析Excel中用户提交的数据可能会在解析过程中转型失败,对于这类很难清楚的明确到在哪里会出现错误,可用异常来控制。

        

posted @ 2022-07-17 12:15  小大宇  阅读(18)  评论(0编辑  收藏  举报