Rebuilding serialization in big words 16: Super Big Functions
Things always look like this: When we endure, and endure a legacy system ...... After accumulating a day, I can't bear it anymore. I can't bear it anymore. Rebuild it !!! This happened. However, at this moment, you suddenly find that the reconstruction work is too complicated to start from. The accumulation of problems is one after another, and the design of the desired changes is full of thoughts. There is an idea here. There is an idea there. I want to do everything, but I can't do anything. It's a mess in my mind. At this time, there is no reasonable step, clear plan, and it is very dangerous to do nothing, it will bring an unpredictable future for your restructuring. Countless experiences tell me that no matter what system or architecture is used, it is certainly correct to start from decomposing large functions.
A big function is a super method that is complex in business logic, has a lot of program code, and has a headache once it is mentioned. Super Big functions are hard to understand, making maintenance and change more difficult, and are undoubtedly the hardest hit by software degradation. It may not be complex at first, and its logic is clear and easy to understand. But with the business logic changing, it keeps adding code to it and adding some unreasonable designs. After a long time, it becomes more and more bloated, and super big functions are generated in this way. The emergence of super-large functions has internal objective reasons. Why? As we have discussed earlier, the Objective Pattern of software development is that the business logic is becoming more and more complex. As the business logic becomes more complex, the correct method is to reconstruct and optimize our code in a timely manner. Unfortunately, few people are aware of this. The result is that as the business logic becomes more complex, people always add new code to the original program structure. With the addition of new code, the original clear and simple programs become more and more complex and difficult to understand. Because of this, super big functions become a common problem in the legacy systems of most software companies. Let's use HelloWorld as an example to develop its history.
As described in Chapter 3 above, the initial HelloWorld program is as follows:
/** * The Refactoring's hello-world program * @author fangang */public class HelloWorld { /** * Say hello to everyone * @param now * @param user * @return the words what to say */ public String sayHello(Date now, Stringuser){ //Get current hour of day Calendar calendar =Calendar.getInstance(); calendar.setTime(now); int hour =calendar.get(Calendar.HOUR_OF_DAY); //Get the right words to sayhello String words = null; if(hour>=6 &&hour<12){ words = "Goodmorning!"; }else if(hour>=12&& hour<19){ words = "Goodafternoon!"; }else{ words = "Goodnight!"; } words = "Hi,"+user+". "+words; return words; }}
Dozens of lines of code, simple and clear. Then it began to change. First, Greetings about time became complicated. Some special holiday questions were added, such as the new year greeting "Happy new year! ", Valentine's day greetings" Happyvalentine's day! ", 38 women's day greetings" Happy women 'sday! And so on. At the same time, greetings from the day have become more refined. For such a requirement, the IT siege lions start to rewrite with the keyboard:
/** * The Refactoring's hello-world program * @author fangang */public class HelloWorld { /** * Say hello to everyone * @param now * @param user * @return the words what to say */ public String sayHello(Date now, Stringuser){ //Get current month, date andhour. Calendar calendar =Calendar.getInstance(); calendar.setTime(now); int hour =calendar.get(Calendar.HOUR_OF_DAY); int month =calendar.get(Calendar.MONTH); int day = calendar.get(Calendar.DAY_OF_MONTH); //Get the right words to sayhello String words = null; if(month==1 &&day==1){ words = "Happynew year!"; }else if(month==1 &&day==14){ words = "Happyvalentine's day!"; }else if(month==3 &&day==8){ words = "Happywomen's day!"; }else if(month==5 &&day==1){ words = "HappyLabor day!"; …… }else if(hour>=6&& hour<12){ words = "Goodmorning!"; }else if(hour==12){ words = "Goodnoon!"; }else if(hour>=12&& hour<19){ words = "Good afternoon!"; }else{ words = "Goodnight!"; } words = "Hi,"+user+". "+words; return words; }}
The amount of code starts to double. Then, the customer requests that all user information should come from the user table of the database, and designs the greeting rule table. All greetings about time should come from the query of the table. In this case, we continue to expand the sayHello () method:
/*** The Refactoring's hello-world program * @ author fangang */public class HelloWorld {/*** Say hello to everyone * @ param now * @ param user *@ return the words what to say */public String sayHello (Date now, longuserId) {// Get database connection. try {Class. forName ("oracle. jdbc. driver. oracleDriver ");} catch (ClassNotFoundException e1) {throw newRuntimeException (" No found JDBC driver ");} St Ring url = "jdbc: oracle: thin: @ localhost: 1521: helloworld"; String username = "test"; String password = "testpwd"; Connection connection; try {connection = DriverManager. getConnection (url, username, password);} catch (SQLException e1) {throw newRuntimeException ("Connect database failed! ");} // Get current month, date andhour. calendar calendar = Calendar. getInstance (); calendar. setTime (now); int hour = calendar. get (Calendar. HOUR_OF_DAY); int month = calendar. get (Calendar. MONTH); int day = calendar. get (Calendar. DAY_OF_MONTH); // Get the right words to sayhello String words = null; String greetingRuleSql = "select words, month, day, hourLower, hourUpper from greeting_rules"; try {Prepar EdStatementstatement = connection. prepareStatement (greetingRuleSql); ResultSet resultSet = statement.exe cuteQuery (); while (! ResultSet. isLast () {intmonthOfRule = resultSet. getInt ("month"); intdayOfRule = resultSet. getInt ("day"); if (month = monthOfRule & day = dayOfRule) {words = resultSet. getString ("words"); break;} inthourLower = resultSet. getInt ("hourLower"); inthourUpper = resultSet. getInt ("hourUpper"); if (hour> = hourLower & hour
This is a very simple example, but we can see that it has expanded from just 10 rows to more than 60 rows, expanding by 4 times. In the last version, sayHello () is responsible for connecting to the database, querying data, obtaining the month, date, and hour of the current time, and determining the corresponding business logic, make the program quite complex. We can continue to imagine that if we continue to propose new requirements, such as multi-language support and multi-database support, the program quality will continue to decline until we are intolerable.
The most effective way to solve the super big function problem is to break down the function step by step and restore its proper optimization structure. In this process, the commonly used refactoring method is called "ExtractMethod )". Refactoring is a process of exploration, because we are always not familiar with the system to be restructured at first, and there is no design document (even if there is one, right? Let's talk about it ), people who are not familiar with the system (even if they just don't understand it ). You started to take over the system only after receiving the task. Your understanding of this system is just a cat. At this time, the extraction method is the one we start to explore and understand the most effective tool of this system. It is often done as follows:
When reading a large function, we can consciously and unconsciously segment the code and write comments for a code with relatively independent functions. Sometimes we may also need to adjust the code order and put more associated code together, such as putting the declaration of variables together with the Code actually used by variables, or put the code with obvious positive and negative relationships together. This adjustment is a good start because it makes our code orderly and readable.
Then we can use the "extraction method. We extract the code that is segmented and annotated from the original function and put it in another independent function to get an easy-to-understand name for this function. This is a very important habit. I often think about it for a long time to give a function a correct name, or even modify it several times. Don't think this is a waste of time, it is also an important part of code optimization. But at first we may not have a deep understanding of this code, so we often choose to name it with the result variable. With our deep understanding of this code, we can use the "RenameMethod" method in refactoring to rename it based on its code intent (many development tools, such as eclipse, all support This refactoring method, which allows you to synchronously modify all calls to this method while renaming ). After this extraction of the code segment, the original code becomes a call to this new function.
For example, the original program of a real legacy system is written as follows:
...... IntiCtbz =-1; ElecObjelecObj = null; // obtain the processing time intiCzyf = Integer. valueOf (Stream [9]). intValue (); StringczMonth = String. valueOf (iCzyf % 20 ). toString (). trim (); StringczYear = String. valueOf (iCzyf-(iCzyf % 20)/20 + 2000 ). toString (); if (czMonth. length () = 1) {czMonth = "0" + czMonth;} longlBlsj = Long. valueOf (czYear + czMonth ). longValue (); Stringdqyear = sysDate. toString (). substring (0, 4 );......
This code is not well written, and there are many things we need to optimize. But remember the "small step" principle. This is not the time to solve other problems. Now we first use the extraction method to optimize the program structure. After extraction, the bold section above is changed to the following:
...... IntiCtbz =-1; ElecObjelecObj = null; // obtain the processing time intiCzyf = Integer. valueOf (Stream [9]). intValue (); longlBlsj = getBlsj (iCzyf); Stringdqyear = sysDate. toString (). substring (0, 4 );......
The bold part is the rewritten content, and the extracted content is put into an independent function:
/*** @ Param iCzyf * @ return get processing time */private long getBlsj (int iCzyf) {String czMonth = String. valueOf (iCzyf % 20 ). toString (). trim (); String czYear = String. valueOf (iCzyf-(iCzyf % 20)/20 + 2000 ). toString (); if (czMonth. length () = 1) {czMonth = "0" + czMonth;} return Long. valueOf (czYear + czMonth ). longValue ();}/*** @ param iCzyf * @ return get processing time */private long getBlsj (int iCzyf) {String czMonth = String. valueOf (iCzyf % 20 ). toString (). trim (); String czYear = String. valueOf (iCzyf-(iCzyf % 20)/20 + 2000 ). toString (); if (czMonth. length () = 1) {czMonth = "0" + czMonth;} return Long. valueOf (czYear + czMonth ). longValue ();}
We do not have a deep understanding of this code when naming this newly created function. In the original function, the result of executing this code is to obtain the result variable lBlsj, that is, the processing time. Therefore, we should first name the function getBlsj. But in the subsequent work, we gradually realized that it not only can get the processing time, but also can get a lot of time. In essence, it converts time from one representation to another. Therefore, we use the rename function of the development tool to name this function transformDate, other code that calls it is also modified. The renamed function is not only used to obtain the processing time, but also applied to the processing of other business codes.
The extraction method can be large or small. You can extract hundreds of lines of code or extract only a few lines. But in any case, the extracted code must be functionally cohesive, that is, they execute a clearly stated and clear function. At the same time, the extracted code must be a clear function for execution, rather than multiple. It can be large or small, and can be divided into multiple functions, but at least logically, these functions are part of this large function.
The extraction method is a process of exploration. The key is where the red line is located, that is, the scope of the extracted code. Multiple rows and one row are also true. The key lies in the function we extracted. How do we define its functions. In public, there is no final conclusion, so the extraction of methods is often inverse. At first, we extracted the data according to one idea. Later, we thought it was wrong. Therefore, we put it back into the original function, re-divided it, re-extracted it, and repeated times.
Once, when I reconstructed a Servlet, the extracted function was executing a large segment of business logic operations. However, after a series of business operations, the original program will convert the return value into binary code and write it into response, and return the front-end. At first, I extracted the entire section. However, it is quite awkward that response must be passed in when passing parameters, which makes the business logic coupled with the Web application environment, which is not conducive to future optimization and compilation of automated testing. Then I restored this code, extracted it again, and left the response write part in the original function, while the red line was drawn after the business logic operation and before the response write. The newly reconstructed functions are decoupled from web applications and are ready for further optimization. The reconstruction process tests the capabilities of developers and requires repeated exercises and research.
In addition, the extraction method is like nuclear fission and starts to split from a function to several functions. The decomposed functions are split into several other functions. At the same time, the extraction method is always split in a class. When the decomposition of this class reaches a certain level, the next thing is the fission of classes. One Class is divided into multiple classes, and then the classes are decomposed ...... Class decomposition we use another method-"Extract Class", which will be described later.
Refactoring is a series of equivalent transformations, and the extraction method is the most typical example of these equivalent transformations. Extract A piece of code from the original function. The code is still the code, but the program structure is changed. This ensures the security and reliability of the reconstruction process.
Big talk rebuilding serialization home: http://blog.csdn.net/mooodo/article/details/32083021
Note: I hope that the author or source should be noted to show my respect for the author when I reprint this article. Thank you!