CompSci 307 Fall 2021 |
Software Design and Implementation |
This exercise is intended to help you to practice using Design Patterns to help organize and justify your design choices as well as direct your Refactoring.
At the end of lab, use Gitlab's Merge Request from your pair's forked repository's master
branch back to the original organization repository's master
branch to submit your group's 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_patterns - participating NETIDs".
In your pairs within project team, work together using Pair Programming, with separate cloned repositories or use IntelliJ's Code With Me feature, to practice removing different kinds of hardcoded assumptions from your code by turning them into values:
On Gitlab, one person should fork the original project lab_patterns
into their own repository to allow pushing your group's changes:
Settings -> Members
to add your partner so both people in the group can access the same repository
Maintainer
role in the project and choose Add to Project
git clone
to make a local copy of the one forked repositoryTo ensure everyone does some coding, 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).
After you think each numbered task below is complete, make a GIT commit
with an appropriate comment (i.e., there should be at least 7 commits in your project history). After each sub-project is complete, push
your changes up to Gitlab (i.e., there should be at least 3 pushes done).
Java came out around the same time as Design Patterns, so it was heavily influenced by them and, in fact, used many of their names directly for interfaces and classes (making it very confusing since patterns are intended to describe how multiple classes interact rather than one single class or abstraction).
Start by considering some real life examples from Java:
Iterator
and Iterable
, also Scanner
LocalDate
and OpenJFX Color
DocumentBuilderFactory
Comparator
AbstractList
and AbstractSet
Calendar.Builder
and Locale.Builder
Observer
and Observable
, also OpenJFX's EventHandler
Reader
and Writer
Look at the documentation or implementation of these classes, compare them to the standard description of the Design Pattern they implement. If there is something you like or dislike or something your learned about design during the discussion, make sure to back it up with a specific example.
Summarize the team's discussion for this part in the file DISCUSSION.md
included within the lab repository.
You already know how to implement Design Patterns since they use the basic Java skills and the SOLID design principles you have you have learned and practiced throughout the semester. In fact, you have likely already implemented at least one Design Pattern in your code without knowing there was a "name for it". Thus, Design Patterns are really just a good shorthand for referring to how a group of classes work together to achieve a design goal as well as describing the implementation's design trade-offs since just about any abstraction you create has a design goal.
Still, learning about Design Patterns can often open up your thinking about different ways to solve a design problem you are having — even though, as you can see from these examples, sometimes the final implementation can seem very simplistic:
Using the code in the project's maze
package, complete the following refactoring to implement the Template Method pattern as the SearchAlgorithm
superclass's step()
method and reduce the code duplication in its subclasses.
step()
method of the four SearchAlgorithm
subclasses in order to try to create a single common implementation in the SearchAlgorithm
class.
protected
methods that you will create to capture the differences in how the solvers implement the algorithm's common actions.cleanUp()
method called at the top of the step()
method: some algorithms respond by doing nothing (RandomWalk
and DFS
) and some by marking the path followed to the goal (BFS
and Greedy
).protected
method in each subclass as needed (with its existing code from its version of the step()
method) SearchAlgorithm
class with calls to the appropriate protected
method to generalize its version of the step()
methodstep()
method completely from each subclass, so each has no public
methods of its own (only constructors and implementations of the protected
methods that differ from the superclass's default implementation if there is one)The Template Method Pattern will likely be important in your game engine to help you create plugin points for your framework.
Using the code in the project's matrix
package, complete the following refactoring to implement the Iterator pattern in a simplified version of part of the Simulation project.
MatrixIterator
by implementing Java's expected Iterator
methods correctly (you may introduce any instance variables you want to keep track of the current element to return)
hasNext()
method to return true only if there is at least one more element availablenext()
method to return the next sequential element in the matrix (i.e., going to the next row when necessary)main()
method of the Main
class to verify your implementation basically works The Iterator Pattern can be used beyond just basic data structures, to access steps in an algorithm or levels in a game — even over repeated calls rather than directly in a traditional loop.