Exercise 6: Design II

This exercise will help you practice and demonstrate your ability to perform basic design tasks using multiple forms of polymorphism to address design constraints. Again note that these tasks are not focused on syntax or algorithms but rather on problem solving.

Once again, there are rules.

Note also that some tasks may be graded qualitatively. For those tasks, producing functioning code alone may not suffice. Instead, you must also show good judgement and design in your code in order to receive full credit. Consider the guidelines we have discussed in class.

A template for the project is available here. You should not add new files to the template or they will not be graded. All of your solutions should modify code in the lib/solutions directory. This helps automated checking for adhering to the rules.

Note as well that normal plagiarism detection will be applied. Do not share your code with or show your code to other students.

Task 1 – Cleanly adding structure to a crazy world

In this task you will focus on combining multiple forms of polymorphism in order to make a design that provides a clear and clean API to the user. Successfully completing the task will involve combining multiple forms of polymorphism, but identifying where to use different forms can largely be done in an incremental fashion.

You must design a Structure class. A Structure is simply a container that can hold a sequence of heterogeneous elements. This initially sounds trivial, but the interface of a Structure should provide for flexible and easy usage. Achieving those properties adds internal complexity. In addition, Structures should be assignable and copyable while maintaining ownership over their contents. A Structure should support the following operations:

Your Objective

You will implement the Structure class. You can provide and declarations required in task01.h and you may place additional code in task01.cpp.

You will also implement a printElement function overload for Structures themselves. Note: This means that Structures can contain Structures, and the entire construct is hierarchical. This should simply use the print method of the passed in Structure.

Your solution must be able to support adding any element that is assignable, copyable, and has a printElement overload, even if it is not present in the provided code. In other words, you code should again be extensible without modification. It should be open and closed.

You will find tests for this task in the unit01-*.cpp unit tests and corresponding binaries. Additional tests that add new types to the Structure may be used during grading.

Consider: This design hard wires a single printing behavior into the structure. Can we make that behavior itself customizable within this design? If so, how? If not, why not?

Task 2 – Walking the same paths in new ways

In exercise 5, you explored implementing Dijkstra's shortest paths algorithm using inheritance to provide an interface that allows for "programs with holes". Now, you will explore the same problem again, but you are not allowed to use inheritance within your solution. You may feel free to revisit your previous code and tweak it to the new use case. Observe that you must still achieve "programs with holes", and because your solution must be able to work with different types of graphs, this means you will be using polymorphism. Recall that we discussed four forms of polymorphism in class. We saw one in particular that can provide flexible, efficient solutions to this problem. Specifically, approaches based on traits, analogous to type classes, provide a good direction.

Instead of working on a concrete Graph type, your shortest paths algorithm will be able to simply accept different types of graphs as if the implementation were overloaded for them. Nonetheless, you should only implement Dijkstra's algorithm once and reuse it for each graph kind. By defining a type trait that captures the behaviors you require, the algorithm should work transparently. It can also be extended to entirely new types of graphs without modifying any of the code you write in task02.h.

Note that type traits require defining a specialization in order to extend support to a new type of graph. You will need to support the same two types of graphs considered in exercise 5 by defining specializations for them.

Your objective

Define a function called shortestPath in task02.h that returns the list of nodes in the shortest path from a given source node to a given target. When the source and target are the same, the list should contain only one copy of that node itself. When there is no path from the source to the target, the list should be empty. Notice that you may not know all of the nodes of the graph in advance. NOTE: In contrast to the function from exercise 5, this one should take three arguments. The first will be the type representing the graph. The second should be the source node. The third should be the target node.

You must use type traits to support a generic interface. You can find the two kinds of graphs from exercise 5 again in Support.h. You should define specializations for these graphs to enable your shortestPath function to work transparently when provided appropriate arguments.

You can work from the provided tests to clarify examples and behavior. You may also be particularly interested in using the weight calculations in unit02.cpp of exercise 5.

Submitting

Create an archive of your solution by changing into the directory containing your project template and running:

tar zcvf e6.tar.gz se-design-ii-template/

You can then upload this archive to CourSys.