Refactoring VideoRental

The code in the project VideoRental is working, but could use some design improvements. You are to make the following refactorings to the code. As with all refactorings, these changes should not change the behavior of the code, only the design.

Each code change will be submitted to your part of the repository with a subversion comment explaining what you have done.

Since we are trying to only change the design and not the behavior, we need some way to test the program. The Makefile has an option that will perform unit testing. Enter the command "make test". You will use the command to check that you haven't made any changes to the behavior.

Each individual refactoring must be committed separately, with a proper message in the subversion comment.

Do not, under any circumstances, add any new behavior.

  1. Before we refactor, we need to set up unit testing. I have done most of this, but the unit testing for the class Rental is not done. Finish this, following the examples from the other unit tests, and commit your changes. You can commit the unit testing changes individually, or in one commit. You can also add test cases to the other unit tests.
  2. Long methods are difficult to comprehend during maintenance. In addition, they are difficult to extend. The method statement is too long. Perform an Extract Method Refactoring to replace the code in the switch statement with a call to a new method amountFor that will do the work of the switch.
    Make sure to perform testing after each change.
  3. The parameter name each of the method statement is not very clear. Perform a Rename Parameter Refactoring to rename this parameter to aRental.
  4. The method amountFor is not really part of the responsibilities of the class Customer. It is misplaced. Perform a Move Method Refactoring to move this method to the class Rental.
  5. The method name amountFor is not very descriptive. Perform a Rename Method Refactoring to change this method name to getCharge.
  6. The more local variables used the more difficult it can be to understand a method. The variable thisAmount in method statement is set and never changed, and is used twice. Eliminate this variable with a direct method call by a Replace Temp with Query Refactoring.
  7. Determining the frequent renter points should probably be a responsibility of class Rental rather then class Customer. Using a Extract Method Refactoring, create a new method getFrequentRenterPoints in class Rental and call it in method statement to get the frequent renter points.
  8. Loops that do more than one thing at a time are more difficult to comprehend, and more difficult to extend in the future. The loop in method statement is performing multiple duties; including accumulating the total charge for all movies. Perform a Replate Temp with Query Refactoring to eliminate the variable totalAmount by creating a private method getTotalCharge in class Customer. Use a call to this new method where totalAmount is being output.
  9. Perform a Replace Temp with Query Refactoring to eliminate the variable frequentRenterPoints by creating a private method getTotalFrequentRenterPoints in class Customer. Use a call to this new method where totalAmount is being output.
  10. The method getCharge in class Rental accesses more information in class Movie than it does in class Rental. This is a classic symptom of a method that is in the wrong place. Perform a Move Method Refactoring to move the functionality of the method getCharge into a new method of the same name in the class Movie. Leave the method getCharge in class Rental and have it call the new method in class Movie. It will be necessary to pass the number of days rented.
  11. The method getFrequentRenterPoints in class Rental accesses more information in class Movie than it does in class Rental. Using an Extract Method Refactoring move the functionality of the method getFrequentRenterPoints into a new method by the same name in class Movie. Leave the method getFrequentRenterPoints in the class Rental and have it call the new method in the class Movie.
  12. The methods getCharge and getFrequentRenterPoints both contain things that may vary with type (Movie) and should probably be kept together. Nothing to change yet, just an observation.
  13. The method getCharge is in the class Movie. It uses a switch according to the movie's category in the charge calculation. If a new category is added the switch will need updated. The method getCharge should behave according to the movie's category. There is a problem that movies will change category, e.g., a movie will not be a new release forever. Nothing to change yet. Just another observation.
  14. When creating a hierarchy for common behavior it is common to use an abstract class. An abstract class does not have any implementation for its methods. It serves as the base class for a hierarchy. Add an abstract class Price in the file Movie.hpp. This will serve as a base class for new classes, and is the start of the Replace Type Code with State/Strategy Refactoring.
  15. Currently the price code is an int. We will need to move from an int to type Price. Nothing to change yet.
  16. Define in Movie.hpp the following classes: RegularPrice, ChildrensPrice, and NewReleasePrice. Have all of them inherit from the class Price
  17. Declare a pure virtual method getPriceCode in class Price. Define a virtual method getPriceCode in each of the subclasses that returns the corresponding category (ex. Movie::CHILDREN for the class ChildrensPrice).
  18. Now we need to make the changes that will use our new types for the method price_code in Movie.
  19. Apply the Move Method Refactoring to the method getCharge to move it from the class Movie to the class Price. Don't forget to make it virtual. The method getCharge in the class Movie should delegate (i.e., call and let the other method do the work) to the method getCharge in the class Price.
  20. Now start a Replace Conditional with Polymorphism Refactoring. Override the method getCharge in the class RegularPrice. Use the logic from the switch, comment out the case in the switch, then remove from the switch.
  21. Override the method getCharge in the class ChildrensPrice. Use the logic from the switch, comment out the case in the switch, then remove from the switch.
  22. Override the method getCharge in the class NewReleasePrice. Use the logic from the switch, comment out the case in the switch, then remove from the switch.
  23. The method getCharge in the class Price is not longer directly used, so make the method a pure virtual function. This will make the class Price abstract.
  24. Keep the method getFrequentRenterPoints together with the method getCharge and move it to the class Price also.
  25. Using the Move Method Refactoring move the method getFrequentRenterPoints to the class Price.
  26. Only new releases treat frequent renter points differently. Override the method getFrequentRenterPoints in the class NewReleasePrice.