This module will explore design solutions that incorporate design patterns an inheritance and introduce the Visitor design pattern.
After this module you should:
- Be able to use the Visitor Design Pattern effectively;
- Be able to determine when inheritance can be effectively used within different design patterns including the Visitor, Decorator, Composite, Command, and Strategy patterns.
- Be able to correctly instantiate design patterns with inheritance.
We will explore how to combine design patterns by creating a design for a hypothetical mobile robotics system. In this system, a Robot
class represents a three-wheeled robot with two active wheels and one free wheel that can also rotate around a pivot. The Robot
class provides very basic control primitives through a method void moveLeftWheel(double pRadians)
a similar method for moving the right wheel, and a similar method for moving both wheels in synch. The pRadians
parameter specifies how much to turn the wheel, e.g., pRadians=2*PI
turns the wheel a full circle.
We want to expand this design to implement the following requirements:
- It should be possible to define higher-level commands for the robot in terms of the basic primitives. For example it should be possible to define a "Forward Move" command that moves both wheels by the same amount. The number of different commands should be extensible;
- Commands should be parameterizable, e.g., "move forward 1 meter" vs 2 meters, etc.
- Any command should be reversible;
- Commands should have a name that can be discoverable at run time. For example, a "Move forward 1 meter".
- It should be possible to aggregate commands into more complex "macro commands". For example, a "Back and Forth" command could involve a forward move followed by a backward move.
- It should be possible for components in the system other than the robot to be notified of commands issued on a
Robot
object. Three components interested in robot commands include aCommandLogger
that prints all commands with a time stamp,CommandRecorder
that can be issued a request to record commands (or to stop recording them), and to control the robot to replay the recording; aRobotConsole
component that visually shows the path of the robot in a graphical user interface. - The system should remember the last command issued to a robot, and provide a convenience method
reexecute()
to re-execute this command. The reexecution of the command should be considered a new, separate command.
In addition to these requirements, the final design should exhibit a number of qualities:
- Effective code reuse: the design should limit code duplication;
- Loose coupling: classes should not depend on interfaces they do no need;
- Robustness: The likelihood of
NullPointerExceptions
should be, well, null.
For maximum learning effectiveness, I recommend peeking at the answers only after giving the problems an honest try.
- Add Visitor support to the class hierarchy below by completing the class diagram. Include a
PrintVisitor
as part of the design. Using a sequence diagram, show a scenario of a traversal through a directory object with the print visitor. ThePrintVisitor
prints the name of each file it visits. Implement a mock-up of the design in code, and use the debugger to validate your sequence diagram. - Extend the file system class hierarchy to include a
HiddenDirectory
class that is a subclass ofDirectory
. For the purpose of this exercise, a hidden directory behaves just like a directory, but prints the name as"." + getName().
Adjust both the diagrams and the code in consequence. - Instead of using a subclass for
HiddenDirectory
, implements this feature using the Decorator Design pattern. Adjust both the diagrams and the code in consequence. - Implement a
DeleteVisitor
that find a file with a name passed as parameter to the visitor, and delete all its children, if the file is a directory or a symbolic link that refers to a directory. Once this works, try changing the code so that the specifiedIFile
also gets deleted. Is this a good idea? - Run the University Demo. What is the order of traversal implemented in
SearchVisitor
? What happens if two committees with the same parent node have the same name? Use the debugger to confirm your answer. - Experiment with different traversal orders for the visitor.
- With the University example, implement a
CommitteeDepthVisitor
that can discover the sub-committee depth of a committee that matches an input query. For example, a root committee would have value 0, a sub-committee, 1, and a sub-sub-committee 3. - Solve the review question by producing a class diagram, sequence diagrams to illustrate a command execution, and implement a mock-up of your solution.
Unless otherwise noted, the content of this repository is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.
Copyright Martin P. Robillard 2017