|CPSC 329||Software Development||Fall 2017|
This lab gives you some practice working with the key elements of the MVC pattern. In particular, you will refactor the maze solver code to better support the Single Responsibility Principle (and separate model from view) and will then implement the Observer pattern to decouple the maze solving from the user interface (and allow greater flexibility in user interfaces).
Successful completion of this lab means that you have practice employing key elements of the MVC pattern:
Work in pairs (and one group of three) to complete this lab. Only one person needs to carry out the steps in the lab but everyone in the group should make sure they understand what is going on. There is no limit on collaboration with others, but you need to make sure that you understand for yourself how (and why) to do things.
(*) will be accepted up to 11:59pm without penalty
One handin per group.
To hand in the lab, create a handin tag as directed. Make sure the names of your group members are in the commit comment.
Create a project for this lab:
Create a new Eclipse project called lab11.
Import the files from /classes/cs329/lab11 into the project. Make sure that the five folders under src (io, main, maze, solver, and ui) end up within the src directory in the project and that the six mazeN.txt files end up under the top-level project directory (not inside src).
Share your project into the repository. (Name the folder lab11.) Remember to choose the correct repository layout, and to check the structure in the repository after sharing.
The maze solver code is what you worked with in lab 10, refactored to encapsulate what varies using composition. You will only need to work with the solver and ui packages.
MazeSolverGUI has two responsibilities - it keeps track of the status (discovered, explored, etc) of each cell in the maze as the solver works, and it handles displaying that information on the screen.
Split this functionality into two classes: SolverData should store the marks, counts, and "discovered from" information and should have the necessary getters and setters, while MazeSolverGUI should only handle painting according to the current marks.
Notes: SolverData should go into the solver package. Move the elements of MazeSolverGUI related to marks, counts, and "discovered from" - the instance variables, the constants for the types of marks, and the various setter and getter methods - over to SolverData. (Remove the redraw() calls from the methods moved over to SolverData - this functionality will be dealt with in the next section below.) MazeSolverGUI should retain only elements related to drawing; note that you will need to convert direct references to instance variables to the appropriate getter calls. MazeSolver should create the SolverData object and tell the GUI about it. Fix up references as needed so that methods are invoked on the correct objects.
The program should compile and run after you do this, though you won't see anything in the display as the solver runs because the GUI is no longer being updated. (You should, however, see the statistics printed out once the maze has been solved.)
The goal with the Observer pattern is to decouple the consumer of the information (the display) from the producer of the information (the maze solver) so that the consumer can change without affecting the producer. To implement the Observer pattern:
Remove any knowledge of the GUI or any other UI from MazeSolver: remove the MazeSolverGUI instance variable from MazeSolver along with the relevant references (including the whole printStats method).
SolverData stores the state so it should extend Observable; MazeSolverGUI wants to know about changes to that state so it should implement Observer. For the update method, MazeSolverGUI should call its redraw method.
Observers should be notified of changes in state: add setChanged() and notifyObservers() calls in the appropriate places in SolverData. (Call setChanged() whenever something has changed, but only call notifyObservers once at the end of the method - only one notification is needed even if multiple values change, but you want that notification to occur after all of the changes have been made.)
The main program now needs to create the two pieces (the solver and the GUI) and link them together by adding the GUI as an observer...except that the GUI should be observing the SolverData object (not MazeSolver) and that object is wrapped by MazeSolver and is only initialized after the maze has been set. Some bookkeeping:
Add a getter to MazeSolver which returns its SolverData object.
Now the Observer pattern implementation can be completed:
Modify the MazeSolverGUI constructor so that it adds itself as an observer to the SolverData object.
In the main program (do this for each of DFS, BFS, and best first), create an instance of MazeSolverGUI after the maze has been set. Also remove the printStats call since MazeSolver no longer has that operation.
(This design is a bit awkward - there are some robustness issues because a SolverData object is only created after the maze is set and the GUI and the solver must have the same maze object.)
Decoupling the user interface from the solver makes it possible to add new ways of viewing the solver state:
Create a class StatsPrinter in the ui package. It should implement Observer and its update method should print out the current statistics (using SolverData's printStats() method). Have its constructor add itself as an observer for the SolverData object.
Update each of the main programs to create an instance of StatsPrinter.
The program should now compile and run, with the maze display and printed stats updated as the maze is solved.
(this section is optional)
Address the awkwardness identified above: currently a SolverData object is only created after the maze is set, and the GUI and the solver must have the same maze object. Give some thought to the design so that your solution is as elegant as possible while maintaining the loose coupling between the solver and the GUI.
Make sure that you've committed everything to the repository.
Create a tag handin to hand in your code. Be sure to include the names of your group in the commit comment.