zoukankan      html  css  js  c++  java
  • 我的一次重构实践

         

     重构,在我们的程序设计中真的是非常重要的东西,它的作用,甚至可以说是魔法,能让我们的代码焕发生机,散发出优美的味道。因为重构并不能说是程序语言的语法,所以对于重构的介绍在市面上是比较少的,尤其是真正掷地有声的权威著作,只能靠我们在自己的每次实践中不断实践,不断思考。就以我自己的一次重构经历来说。
           这次我是打算将传送过来的字符串按照一定的方式输出到控制台,所以,做的是解析字符串的工作。这种工作本来可以是用正则表达式来做的,但是因为正则表达式这方面,说真的,实在是太让人纠结了,基本上,当我想用时,总是用不好(实际上是焦头烂额!)。而且,这次有了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的人都很熟悉(好吧,我承认,我其实并不是很清楚这个词的真正意义)。之所以要封装,我们一般都能想到的理由就是隐藏实现细节,但是为什么要隐藏细节?一般都会想到,对用户进行隐藏,使他们无需担心具体实现就能放心的使用类或方法。这样子做确实是封装的需求,但是是基于用户角度来思考封装的意义,而程序设计者呢?他们的封装就只是这样吗?我们的封装,将代码的实现细节封装进一个方法或类里,然后让其他方法或类调用,这种思想,比起将相关变量传参的方法来说,好处在哪?其中最主要的原因就是我们如果是传一个对象进来,就会将这个对象的其他东西也会带进来,但是如果是方法调用,就只是调用该类相关的方法而已,不会带来多余的东西。这个话题暂时说到这里,我想,我需要就这个问题再好好想想。
         }
  • 相关阅读:
    希腊字母写法
    The ASP.NET MVC request processing line
    lambda aggregation
    UVA 10763 Foreign Exchange
    UVA 10624 Super Number
    UVA 10041 Vito's Family
    UVA 10340 All in All
    UVA 10026 Shoemaker's Problem
    HDU 3683 Gomoku
    UVA 11210 Chinese Mahjong
  • 原文地址:https://www.cnblogs.com/wenjiang/p/2662175.html
Copyright © 2011-2022 走看看