CompSci 308
Spring 2021
Advanced Software Design and Implementation

Decomposition: Refactoring Code into Multiple Classes

This exercise is intended to help you to learn to critically read code, understand its purpose, and identify ways to improve its design using shorter methods and multiple classes. Along the way, start to build a sense of a reader's expectations about code and what makes one piece of code better than another given a specific set of design goals.

Pair Programming

For this lab and the remaining labs in this course, you will work either in pairs or with your project team.

For the discussion part of the lab, this gives you someone to collaborate and discuss ideas with. For the programming part, it gives you a chance to practice Pair Programming: working closely with another programmer, sharing a "single computer" (a common practice in industry before COVID) that we will simulate using a shared repository. To ensure both people do some coding, you will switch which person actually writes the code every 10-15 minutes (in industry this switch happens only 1-3 times per day). The person who is not actively coding can be advising, suggesting better names, looking up documentation, or searching the Internet for solutions to small problems you are likely to face, but not multi-tasking (i.e., doing their own work or socializing).

Lab Workflow

Here is a review of the steps you will use to work with GIT during lab (as distinct from assigned team projects):

  1. On Gitlab, one person should fork the original project lab_hangman into their own repository to allow pushing your group's changes
    • On the resulting project web page, go to Settings -> Members to add your partner so both people in the group can access the same repository
    • Search for your partner's name and give them a Maintainer role in the project and choose Add to Project
  2. In Terminal, both students should use git clone to copy the one forked repository to their individual machines so they can work on their own separate copies
  3. In IntelliJ, both students should Open the cloned folder to create a new project on their personal machine
  4. In IntelliJ, add the names and NetIDs of everyone in your group at the top of the DISCUSSION.md file included in the repository
  5. As a group, discuss how to change features in the current code and what design issues that reveals using the questions below as a guide
    • Report your ideas, reasoning, and the main conclusions in the file DISCUSSION.md using Gitlab's markdown format
  6. In IntelliJ, refactor the code to improve its design (i.e., edit the code) using Pair Programming
  7. In Terminal, use GIT to
    • git add changed files
    • git commit -m to describe your changes
    • git push changes back to Gitlab
    • git pull changes from Gitlab back to the local machine (if you are not sharing a single computer)
    • Repeat as many times as needed, letting each person try the changes/GIT steps in related chunks
  8. As a group, discuss the refactored design using the questions below as a guide.
    • Report your ideas, reasoning, and the main conclusions in the file DISCUSSION.md using Gitlab's markdown format

Submission

At the end of lab, use Gitlab's Merge Request from your forked repository's master branch back to the original organization repository's master branch to submit your group's answers to the discussion questions and your refactored code. You only need to create one Merge Request, no matter how many commits you make, and you do not worry about potential conflicts since your request will not be approved (it is just an easy way for us to see what changes you made).

Make sure the Title of your Merge Request is of the form "lab_hangman - everyone's NetIDs".

Specification

CompSci 101 currently, and CompSci 201 previously, give an assignment based on the game of Hangman. It is typically given in multiple parts based on what concept students are learning in the course, but the instructions tend to be the same: copy your previous version and update it to implement new kinds of players (either a guesser or executioner). To simulate this in lab, there are three versions of the game that each play slightly differently:

The goal of this exercise is to use the commonalities and differences between the three versions of the Hangman Game classes in the package game to identify what code is specific to guessers and executioners (or generally, players) and extract those differences into separate classes with methods that can be called from the game code instead of being part of the game class. In the end, instead of three classes with heavily duplicated code that represent one concept within the project, you should have the following classes that represent distinct concepts in the project with no duplicated code:

Removing the duplication and decomposing one conceptual class into three will also improve the overall design by separating the game logic from the player logic so that they can be changed easily and independently.

Note you only need to consider the code in the game package, not the util package.

Discussion

In pairs, examine the code from the perspective of how readable it is (i.e., is it easy to find or understand important parts of the code, does it do what you expect, does it require more or fewer comments). While studying this code, also note any good things about the code, that you might keep in the final version as well as smells in the code. If you have any questions about how it works, note those as well and talk to one of the Teaching Team to get your question resolved.

To begin discussing the code's design, spend some time evaluating the code using the following questions as a guide:

Then spend some time separating the game logic from the player logic by identifying candidate code within each game that could be turned into methods to represent a player's behavior in two separate classes (guesser or executioner). Start by considering how would you make a fourth copy of the games with these new players using the current design:

For this discussion, do not worry about the exact Java code needed to implement these players but instead identifying what code in the game needs to be changed and what should stay the same. To guide your discussion, look at what is the same and what is different about the existing code and consider the following questions:

Finally, describe as concretely as you can, what lines of a current game class need to be changed to implement two new players described above.

Report your ideas, reasoning, and the main conclusions in the file DISCUSSION.md using Gitlab's markdown format

Refactoring

Refactoring is the practice of updating a program to improve its design and maintainability without changing its current functionality significantly. An example of refactoring is creating a single method or class that replaces two or more sections of similar code because it reduces the amount of duplicate code within the program or makes the code easier to debug and test. You do not have to worry about updating the algorithmic code itself, instead your goal is primarily about reorganizing the existing code to make it more readable and improve the overall design.

Your group may create (and comment why you choose to create them) any new methods or classes you want to help improve the program. Try to justify each change you make by explaining specifically how it improves the code. Justifications should refer specifically to principles discussed in class or the reading rather than using terms like "clearly/obviously", "good/sucks", or "like/hate".

Periodically discuss when you think it is appropriate to create a commit that represents a related set of changes (i.e., not just at the end of the lab or only each time you switch roles). After you think you have completed a reasonable goal, make a GIT commit with an appropriate comment (there should be at least four commits in your project history). After every two commits, push your changes up to Gitlab so your online repository reflects the work you have done in lab today.

Examine the given program and refactor it based on your group's discussion using the following steps:

  1. Choose one game to refactor, rename it to HangmanGame, and try to extract the methods you identified in your discussion that represent potential player code within that game
  2. Create a Guesser class that implements interactive guessing and update the HangmanGame class to use it instead of its own methods and update the Main class to create the classes appropriately
  3. Create an Executioner class that implements choosing a random secret word and update the HangmanGame class to use it instead of its own methods and update the Main class to create the classes appropriately
  4. Update your Guesser class to implement choosing letters automatically and update the Main class to create the classes appropriately
  5. Update your Executioner class to implement changing the secret word after each guess and update the Main class to create the classes appropriately

Note, within your general player class methods, you may need to use conditional statements to choose between different player algorithms (e.g., between interactive or algorithmic or random) — this is not an ideal solution and we will address that separately in the next few weeks.

After you have done as much as you think is reasonable, describe as concretely as you can how to implement these changes using this new version of the code:

Finally, consider the following questions:

Report your ideas, reasoning, and the main conclusions in the file DISCUSSION.md using Gitlab's markdown format.