我的一次重构实践

     

 重构,在我们的程序设计中真的是非常重要的东西,它的作用,甚至可以说是魔法,能让我们的代码焕发生机,散发出优美的味道。因为重构并不能说是程序语言的语法,所以对于重构的介绍在市面上是比较少的,尤其是真正掷地有声的权威著作,只能靠我们在自己的每次实践中不断实践,不断思考。就以我自己的一次重构经历来说。
       这次我是打算将传送过来的字符串按照一定的方式输出到控制台,所以,做的是解析字符串的工作。这种工作本来可以是用正则表达式来做的,但是因为正则表达式这方面,说真的,实在是太让人纠结了,基本上,当我想用时,总是用不好(实际上是焦头烂额!)。而且,这次有了org.JSON这个库的支持,所以这方面的工作可以说是相对比较方便。废话不多说,就看看我在做这个东西的时候,是怎么感受到重构的美妙的。
         由于我是新手,所以一开始写代码时真的都是非常混乱的,因此,每次写完时,一定都会再重新看一遍,改一遍,哪怕它能通过功能测试。字符串如下:
          String str = "[{\"title\":\"java book\",\"author\":\"json\",\"rating\":3},{\"title\":\"java book\",\"author\":[\"json\",\"fuck\"],\"rating\":3}]";
           这样的字符串表达的是,有两本书,每本书都有一个title,author,rating,而我要做的,就是从里面解析出书的这些信息出来。相信大家都已经注意到了,就是书的author可以是一个字符串数组,也可以不是。接着就是开始解析了。我这里用的是java,编译器是eclipse。我一开始就写下这样的方法:
           Book book = new Book();
           book.parse(str);
           为什么是Book,因为我想写通过单独一本书的测试用例的代码,然后再是一本以上。Book是我自己定义的类,用来存放书的信息。下面就是Book的内部细节:
          
 public class Book {
    private String title;
    private int rating;
    private String[] author;

    public String getTitle() {
        return title;
    }

    public int getRating() {
        return rating;
    }

    public String[] getAuthor() {
        return author;
    }





}
接着就是实现parse这个方法。这个方法的实现并不难,如:
   private static Book parse(String str) throws JSOException{
             Book book = new Book();
             JSONObject json = new JSONObject(str);
             book = parseSingleBook(book, json);
             return book;
         }
  private static Book parseSingleBook(Book book, JSONObject json)throws JSOException{
            book.title = json.getString("title");
            book.rating = json.getInt("rating");
            Object authorObj = json.get("author");
            book.author = parseAuthorObjAsAuthor(book, authorObj);
            return book;
      }
 private static String[] parseAuthorObjAsAuthor(Book book, Object authorObj)throws JSOException{
          if (authorObj instanceof JSONArray) {
                JSONArray jsonAuthor = (JSONArray) authorObj;
                int length = jsonAuthor.length();
                book.author = new String[length];
                for (int i = 0; i < length; i++) {
                    book.author[i] = (String) jsonAuthor.get(i);
                }
         } else {
                book.author = new String[1];
                String singleAuthor = (String) authorObj;
                book.author[0] = singleAuthor;
          }
            return author;
  }


 

 至此就是解决单独一本书这种情况的代码,其中,我已经经过了重构,原本都是写在一个方法里,然后再将其中的代码按照它们要实现的功能放进一个方法里。这种手法就是“提取方法”,通过这样使得我们的代码的可读性更强,哪一部分实现什么功能都能一目了然,并且切实的遵守了代码职责单一的设计原则。但是,这里还是有一个问题,相信大家都注意到了,就是book这个参数我在每个方法中都传进去了。这样子的代码总是令人感到困惑,就是为什么我要将book传来传去呢?于是就有一个问题,有必要吗?其实还真的没有必要,因为通过查看代码就可以知道,之所以传进一个book,是因为我需要一个初始化的Book类。那么我就完全可以就在方法中初始化啊!于是接下来的代码就是像下面这样:
 private static Book parseSingleBook(JSONObject json)throws JSOException{
           Book book = new Book();
            book.title = json.getString("title");
            book.rating = json.getInt("rating");
            Object authorObj = json.get("author");
            book.author = parseAuthorObjAsAuthor(authorObj);
            return book;
      }
private static String[] parseAuthorObjAsAuthor(Object authorObj)throws JSOException{
         String[] author;
         if (authorObj instanceof JSONArray) {
               JSONArray jsonAuthor = (JSONArray) authorObj;
               int length = jsonAuthor.length();
               author = new String[length];
               for (int i = 0; i < length; i++) {
                   author[i] = (String) jsonAuthor.get(i);
               }
         } else {
                author = new String[1];
                String singleAuthor = (String) authorObj;
                author[0] = singleAuthor;
           }
                   return author;
        }
注意第二个方法,我将book.author改为author,那是因为我注意到了一个问题,那就是我这个方法到底传进来的是一个什么东西?无非就是一个String数组!所以我当然可以在里面初始化一个字符串数组。好了,这个方法里面还有一个值得注意的地方,就是(authorObj instanceof JSONArray)这个条件的判断,它其实是判断它到底是一个数组或者不是。这样的判断是没有任何标志来识别的,因为两者完全没有任何联系!所以可以这样做,就是不管它是什么类型,就是先设为一个对象,因为无论数组或者不是数组,本质都是对象,然后再用instanceof 来判断是否是数组的一个实例。
            至于一本以上的情况,基本上都是调用上面的方法来实现的,因为都是一样,只是需要用到循环而已。
            这次实践让我深刻意识到重构的重要性。老实说,一开始我是有很多方法的,两种情况各自对应自己的处理方法,但是后来在重构的过程中发现它们其实都是一样的算法,那么为什么就不能将它们提取为一个方法,然后让它们被调用呢?这样我的代码的逻辑就会更加清楚。接着就是参数列表。基本上,当你看到参数列表超过一个以上,你就会开始思考,是否太多了?这时,就要记住,你的方法的输入是什么,输出是什么。就像我上面最后一个方法,我输入的其实只是一个字符串数组,输出的也是一个字符串数组。所以,这样是否可以就在方法里定义一个数组而不是传参进来呢?这样的想法同样适用于方法的合并,基本上只要输入和输出相同的方法,它们就可能存在可合并的可能性。
           这次的实践还让我对对象的封装有更多的认识。对象的封装,相信只要是学java的人都很熟悉(好吧,我承认,我其实并不是很清楚这个词的真正意义)。之所以要封装,我们一般都能想到的理由就是隐藏实现细节,但是为什么要隐藏细节?一般都会想到,对用户进行隐藏,使他们无需担心具体实现就能放心的使用类或方法。这样子做确实是封装的需求,但是是基于用户角度来思考封装的意义,而程序设计者呢?他们的封装就只是这样吗?我们的封装,将代码的实现细节封装进一个方法或类里,然后让其他方法或类调用,这种思想,比起将相关变量传参的方法来说,好处在哪?其中最主要的原因就是我们如果是传一个对象进来,就会将这个对象的其他东西也会带进来,但是如果是方法调用,就只是调用该类相关的方法而已,不会带来多余的东西。这个话题暂时说到这里,我想,我需要就这个问题再好好想想。
     }
原文地址:https://www.cnblogs.com/wenjiang/p/2662175.html