Software Engineering: Refactoring VideoRental Assignment

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.

The course svn repository is at http://classes.cs.kent.edu/courses/cs43901/svn/001/$USERNAME/cs43901_student and the shared folder is at http://classes.cs.kent.edu/courses/cs43901/svn/001/cs43901_lecture/

Copy the project folder (VideoStore) and make changes committing them in subversion. 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 or change existing behavior.

Do the following:

  1. Read the code over and construct a UML class diagram that represents the design of the system in it's INITIAL current state. Make sure to include all the relationships. Commit this as in your folder as a jpg (you can include the visio file also or what ever tool you are using).
  2. Before refactoring, unit tests need to be set up. Most of this is completed, 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. Commit the unit testing changes individually, or in one commit. You can also add test cases to the other unit tests.
  3. 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.
  4. The parameter name each of the method statement is unclear. Perform a Rename Parameter refactoring to rename this parameter to aRental.
  5. The method amountFor is not part of the responsibilities of the class Customer. It is misplaced. Perform a Move Method refactoring to move this method to the class Rental.
  6. The method name amountFor is not very descriptive. Perform a Rename Method refactoring to change this method name to getCharge.
  7. 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.
  8. 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.
  9. Loops that do more than one thing at a time are more difficult to comprehend and 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.
  10. 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.
  11. 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.
  12. 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.
  13. 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.
  14. 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.
  15. 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.
  16. Currently the price code is an int. We will need to move from an int to type Price. Nothing to change yet.
  17. Define in Movie.hpp the following classes: RegularPrice, ChildrensPrice, and NewReleasePrice. Have all of them inherit from the class Price
  18. 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).
  19. Now we need to make the changes that will use our new types for the method price_code in Movie.
  20. 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.
  21. 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.
  22. 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.
  23. 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.
  24. 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.
  25. Keep the method getFrequentRenterPoints together with the method getCharge and move it to the class Price also.
  26. Using the Move Method Refactoring move the method getFrequentRenterPoints to the class Price.
  27. Only new releases treat frequent renter points differently. Override the method getFrequentRenterPoints in the class NewReleasePrice.
  28. Construct a UML class diagram that represents the design of the system in it's FINAL state. Make sure to include all the relationships. Commit this as in your folder as a jpg (you can include the visio file also or what ever tool you are using).