Code refactoring (vi): Code refactoring full case

Source: Internet
Author: User
Code Refactoring (vi): Code refactoring full Case

Whatever you do, do it finish. In front of the previous 5 blogs about refactoring, which are categorized to introduce some refactoring techniques. Today's blog uses a complete example to summarize the previous refactoring rules, as well as a full stop for the previous blog about refactoring. Today's example builds on some of the examples from the first chapter of the book, "Refactoring, improving the design of existing code." Today's blog is a complete refactoring process from beginning to end. First, we'll give you the code that needs refactoring, then analyze it, and then use the refactoring rules that we shared before to refactor it step-by-step.

Let's talk about the usage scenario for this example (if you have a refactoring book, you can take the example in the first chapter, but the examples in this blog are somewhat out of the way). is a customer to the DVD rental store for consumption, the following program is for the owner to use, based on the different types of DVDs borrowed by the user and the amount to calculate the amount of the user's consumption and points. The requirements are simple and not difficult to understand. Today's blog will give the original code, but also the code that needs to be refactored. Of course, the original code fully meets the requirements and can be executed correctly. Talk less, look at the example first.

A code that needs refactoring

In the first part of this blog, we first give the code that needs to be refactored to complete the above requirements. Then, based on the analysis, we use the refactoring technique we mentioned before to reconstruct it. First, we give the realization of the movie class. There are kinds of movies in the movie Class (Static constants): Ordinary movies, children's movies, new movies, then there are two member variables/constants that are Pricecode (price code), title (movie name), and finally our construction method. The movie class is relatively simple, here do not do too much to repeat.

The realization of the movie class is followed by leasing class rental, the responsibility of this rental class is responsible for the statistics of a film rental time. Below is the lease class, the class is also relatively simple, which has two fields, one is rented movie, the other is the lease time.

The next step is to implement our consumer class, which is the customer class. In the customer class, there is the consumer's name, name, and an array in which the collection of rented movies is in the array. The statement () method is the method that settles the customer's billing information and prints the results. The requirements that we need to understand here are the way in which each movie is priced and the rules for calculating the points.

Movie Price calculation rules:

Ordinary film--2 days including 2 days, each with a charge of 2 yuan, more than 2 days of the portion of the daily fee of 1.5 yuan

New film-3 yuan per day

Children's film-3 days including 3 days, each with a charge of 1.5 yuan, more than 3 days of the portion of the daily fee of 1.5 yuan

Integral calculation rules:

Every single step of the movie points plus 1, the new slice plus 2 per part

The thing in the statement () function is that the calculation of the amount and the calculation of the integral are made according to the calculation rules above, according to the difference of the film rented by the user.

If you look at the code is not very intuitive, I used the startuml simple to draw a UML class diagram to illustrate the above three classes in the dependency relationship. The following are the specific examples:

Before refactoring the code above, we must also have test cases for the code above. Because before each refactoring, we modify the internal structure of the code, and the code module calls outside of the method will not change. So the test cases we create can help to verify that our refactored programs work properly, and that refactoring is also in line with our needs. Below is the test case we created (of course, in iOS development you can use other test frameworks for unit testing, refactoring, unit testing is required). The refactoring code in this blog still uses the test cases below.

1//test case--------------------------------------------------------------------
 2//create user
 3 Let customer = Customer (Name: "Zeluli")
 4 
 5//create movie
 6 let Regularmovie:movie = Movie (title: "Old Cannon", Pricecode: Movie.regular)
 7 Let Newmovie:movie = Movie (title: "Sherlock Holmes", PriceCode:Movie.NEW_RELEASE)
 8 let Childrenmovie: Movie = Movie (title: "Gourd Doll", PriceCode:Movie.CHILDRENS)
 9 
10//create lease data one by one let
rental1:rental = rental (movie : Regularmovie, daysrented:5)
rental2:rental = Rental (Movie:newmovie, daysrented:8)
Rental3:ren tal = Rental (Movie:childrenmovie, daysrented:2)
- 
customer.rentals.append (RENTAL1)
16 Customer.rentals.append (Rental2),
customer.rentals.append (RENTAL3)
, 
result = Customer.statement ()
print (Result)

For the above case, the output of the above test case is as follows. After each refactoring, we will execute the above test code and see if the result is the same as before. Of course, if you're a unit test, you can simply hand over the work of the result check to the assertions in the unit test.

Second, refactoring 1: Splitting the more statement function

1. Use the "Extract Method" principle for the statement () function

In the above case, the most intolerable, that is, the first to refactor is the statement () function in customer. The biggest drawback of the statement () function is that there are too many things inside the function, and the first step we need to do is to split it. That is, the function is simplified and split using the "Extract Method" principle we mentioned earlier. Extracts the modules that can be isolated from the statement (). After analysis, we are not difficult to find the red box below the code is a complete module, one for the unit price calculation, one is to calculate the integral, we can extract the two pieces of code and encapsulated into a new method. In encapsulating the new method, give the new method name an appropriate function name, see the name of the known meaning.

This code below is our extraction of the code in the two red boxes above. When extracting, the data in the statement () function is dependent on the parameters of the new function. After encapsulation, the following method can be called in the corresponding place in the statement function. Below is the function we encapsulate to calculate the current movie amount and calculate the integral. Both of these functions need to pass in an rental object.

    Calculates the amount of the current movie according to the lease order Func amountfor (arental:rental), Double {var result:double = 0//single
                Price variable switch ARental.movie.priceCode {case Movie.REGULAR:result + = 2
                If arental.daysrented > 2 {result + = Double (arental.daysrented-2) * 1.5 } case Movie.NEW_RELEASE:result + = Double (arental.daysrented * 3) Case Movie.chil Drens:result + = 1.5 if arental.daysrented > 3 {result + = Double (A
    RENTAL.DAYSRENTED-3) * 1.5} Default:break} return result }//Calculates the current movie's Integral func getfrequentrenterpoints (rental:rental), Int {var frequentrenterpoints : Int = 0//user integral frequentrenterpoints++ if Rental.movie.priceCode = = Movie.new_release & & Rental. daysrented > 1{frequentrenterpoints++} return frequentrenterpoints} 

After the refactoring steps above, we run a test case or perform a unit test to see if our refactoring process is causing a new bug.

Iii. Refactoring 2: Move the appropriate method into the appropriate class

After the refactoring above, we extracted two methods from the statement () function. After observing the two reconstructed methods, it is not difficult to see that both of these new methods require a parameter, which is the object of the rental class. That is, both methods depend on the rental class, and the current class in which the function is located is not too cold. This occurs because the two functions are misplaced because the two functions are not dependent on the customer class and depend on the rental class, which is sufficient to indicate that both methods should be placed in the rental class.

After our simple analysis, we can decide to put our newly extracted method into the rental class, and the function parameters are removed. Because the function is in the rental class, you can use self directly in the function. After the method of calculating the amount and the method of calculating the integral are moved to the rental class, our rental class is shown below. In the statement () method in our customer, the method in the rental is called directly when calculating the amount and calculating the integral. After this step of refactoring, do not forget to execute your test case and monitor the results of the refactoring correctly.

Iv. refactoring the statement () function again using "Replace temporary variable with query"

After the second and third steps of refactoring, the statement () function in customer is shown below. When calculating the amount and integral of each movie, we call the corresponding method of the object of the rental class. The method below is much simpler and easier to understand and maintain than the first part of our approach.

However, the above code still has a refactoring space, for example, if we want to organize the results in HTML, we need to copy the above code, and then modify the result variable text organization mode. But in this case, many of these temporary variables also need to be copied, which is exactly the same, so it is easy to produce duplicate code. In this case, we need to use the refactoring technique of replace temp with query (the query replaces the temporary variable) to remove the temporary variable from the red box above.

Each of the temporary variables in the red box above we will extract a query method, below is the statement () function that uses the "replace Temp with Query" (the query replaces the temporary variable) rule to refactor, and the extracted two query functions.

After the refactoring of the above steps, our test cases remain the same. After each refactoring we need to call the test cases above to check whether the refactoring has side effects. Now the dependencies between our classes have not changed much, but the methods in the corresponding classes have changed a little. Below is the corresponding class diagram of the code now, because in the process of refactoring, we mainly do the reconstruction of the function, that is, to extract the function, and then put the extracted function into the corresponding class, from the simplified class diagram below can be seen.

Five. Continue to move the corresponding function (move Method)

After the reconstructed code is observed and analyzed, we find that the content in the Getcharge () function in the rental class is more dependent on the movie class than the content in the Getfrequentrenterpoints () function. Because both functions use only the daysrented attribute in the rental class, the contents of the movie are used more than once. So we need to move the contents of these two functions into the movie class more appropriately. So I went on to move on to that part of the story.

The move is to preserve the declarations of the two functions in the rental, create the corresponding function in the movie, move the contents of the function into the movie, and then call the method in the movie in rental. Below is what we have in our movie class after this refactoring. Where the contents of the red box are what we move, and the parameters in the green box need to be imported from outside.

After moving the corresponding method body into the movie class, we need to call it in rental. The appropriate parameters are passed in when the corresponding method is called. Below is the code of this Chinese rental class, the code in the green box is the call to the newly added method in the movie.

Through the refactoring above, our method seems to have found a home. Refactoring is like this, step by step, do not worry, do not move a step always to the good direction of development. If you refactor from the first part of the code to the fifth part, it seems a little difficult. After these indirect processes, the feeling is quite pleasant. Below is a class diagram that passes through our refactoring this time.

VI. replacing conditional expressions with "polymorphic"

When the conditional expression was refactored in our previous blog, it was mentioned that the conditional expression was refactored using the polymorphism of the class. Next we will use this rule to refactor the Getcharge () and getfrequentrenterpoints () functions in the movie class. That is, using the "state mode" that is often used in our design patterns. In this section we do not need to modify the rental class and the customer class, only the movie class, and introduce the corresponding interface and inheritance relationship.

When we look at the structure of the switch-case in the Getcharge () method in the movie class, it is easy to see that the polymorphism of the class can be used instead (see Code refactoring (iv): conditional expression Refactoring rule (Swift version)). The way to do this is to extract the non-available price calculation into our newly created price class, each movie has its own price class, and these price classes all implement the same interface, so that in the movie can use Polymorphic to get the price. The calculation of integrals is the same. Below is the class diagram where we want to implement the structure. The red box below is a new interface and class added on top of the original, and the business logic that the conditional expression handles is placed in our newly added class. This allows us to use the polymorphism of the classes and to follow the "single duty".

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.