重构函数调用-Separate Query from Modifier将查询函数和修改函数分离四

重构函数调用-Separate Query from Modifier将查询函数和修改函数分离四

1.将查询函数和修改函数分离

1.1.使用场景

某个函数既返回对象状态值,又修改对象状态。建立两个不同的函数,其中一个负责查询,另一个负责修改。

1.2.如何做

  • 新建一个查询函数,令它返回的值与原函数相同。
  • 观察原函数,看它返回什么东西。如果返回的是一个临时变量,找出临时变量的位置。
  • 修改原函数,令它调用查询函数,并返回获得的结果。
  • 原函数中的每个return句都应该像这样:return newQuery(),而不应该返回其他东西。
  • 如果调用者将返回值赋给了一个临时变量,你应该能够去除这个临时变量。
  • 编译,测试。
  • 将调用原函数的代码改为调用查询函数。然后,在调用查询函数的那一行之前,加上对原函数的调用。每次修改后,编译并测试。
  • 将原函数的返回值改为void,并删掉其中所有的return语句。

1.3.示例

有这样一个函数:一旦有人入侵安全系统,它会告诉我入侵者的名字,并发送一个警报。如果入侵者不止一个,也只发送一条警报:

  String foundMiscreant(String[] people){
      for (int i = 0; i < people.length; i++) {
          if (people[i].equals ("Don")){
             sendAlert();
             return "Don";
          }
          if (people[i].equals ("John")){
             sendAlert();
             return "John";
          }
      }
      return "";
  }

该函数被下列代码调用:

  void checkSecurity(String[] people) {
      String found = foundMiscreant(people);
      someLaterCode(found);
  }

为了将查询动作和修改动作分开,我首先建立一个适当的查询函数,使其与修改函 数返回相同的值,但不造成任何副作用:

   String foundPerson(String[] people){
       for (int i = 0; i < people.length; i++) {
           if (people[i].equals ("Don")){
              return "Don";
           }
           if (people[i].equals ("John")){
              return "John";
           }
       }
       return "";
   }

然后,我要逐一替换原函数内所有的return语句,改调用新建的查询函数。每次替换后,编译并测试。这一步完成之后,原函数如下所示:

  String foundMiscreant(String[] people){
      for (int i = 0; i < people.length; i++) {
          if (people[i].equals ("Don")){
             sendAlert();
             return foundPerson(people);
          }
          if (people[i].equals ("John")){
             sendAlert();
             return foundPerson(people);
          }
      }
      return foundPerson(people);
  }

现在,我要修改调用者,将原本的单一调用动作替换为两个调用:先调用修改函数,然后调用查询函数

  void checkSecurity(String[] people) {
      foundMiscreant(people);
      String found = foundPerson(people);
      someLaterCode(found);
  }

所有调用都替换完毕后,我就可以将修改函数的返回值改为void:

  void foundMiscreant (String[] people){
      for (int i = 0; i < people.length; i++) {
          if (people[i].equals ("Don")){
             sendAlert();
             return;
          }
          if (people[i].equals ("John")){
             sendAlert();
             return;
          }
      }
  }

现在,为原函数改个名称可能会更好一些

  void sendAlert (String[] people){
      for (int i = 0; i < people.length; i++) {
          if (people[i].equals ("Don")){
             sendAlert();
             return;
          }
          if (people[i].equals ("John")){
             sendAlert();
             return;
          }
      }
  }

当然,这种情况下,我得到了大量重复代码,因为修改函数之中使用了与查询函数相同的代码。现在我可以对修改函数实施Substitute Algorithm (139),设法让它再简洁一些:

  void sendAlert(String[] people){
      if (! foundPerson(people).equals(""))
          sendAlert();
  }

并发问题
如果你在一个多线程系统中工作,肯定知道这样一个重要的惯用手法:在同一个动作中完成检查和赋值。这是否和Separate Query from Modifier (279)互相矛盾呢?我曾经和Doug Lea讨论过这个问题,并得出结论:两者并不矛盾,但你需要做一些额外工作。将查询动作和修改动作分开来仍然是很有价值的。但你需要保留第三个函数来同时做这两件事。这个“查询—修改”函数将调用各自独立的查询函数和修改函数,并被声明为synchronized。如果查询函数和修改函数未被声明为synchronized,那么你还应该将它们的可见范围限制在包级别或private级别。这样,你就可以拥有一个安全、同步的操作,它由两个较易理解的函数组成。这两个较低层函数也可以用于其他场合。

你可能感兴趣的:(【重构代码设计思想】,重构,java,servlet)