Applying Axiomatic Design Concepts to Software Development
Axiomatic Design applies equally well to software. In particular it develops a key principle of independence:
The goal of system design is to reduce the dependencies between subsystems, especially avoiding circular dependencies.
Let’s look at the earlier familiar example that illustrates this issue, which applies equally well to software: the hot-cold faucet example.
Axiomatic technology uses easy to read maps to show how design elements affect the functions of the design. In these dependency maps, each column is a design element, while each row is a function. The cell shows whether the column’s design element affects the function of that row: If so, the cell has an X, if not there is no X. According to theory, a design is independent – that is, contains no circular dependencies – when all the Xs are inside the triangle.
For water faucets, the desired functions are to adjust the flow rate and temperature of the water. This dependency map of the single-lever faucet shows that the up-and-down motion adjusts flow rate only, while side-to-side adjusts just the temperature. In contrast, both valves of the dual-valve design affect both flow rate and temperature, just as we said earlier. We can see in this map that the dual-valve design is not independent because one X is outside the triangle.
Dual knob faucet:
Lever arm faucet:
These principles apply to software. Let’s look at this simple example to understand how this works.
Imagine an application that calculates results from a user’s input and displays the result. The diagram above represents an architecture to solve this problem. The application is split into two logical routines: Main and GUI. The Main routine encapsulates the calculation routines and passes results to the GUI routine to be displayed. The GUI routine displays the calculation output and allows users to change inputs and drive the calculation functions in Main. Because the Main routine calls GUI functions and vice-versa, the code has a circular dependency.
Having identified a circular dependency, we should consider ways to remove it. One way to do this is to separate logically the calculation functionality from the user response functionality in Main. To do this, we can design a new interface, Listener, which encapsulates the user response functionality. Now, Main inherits this interface rather than implementing it directly. The resulting design and corresponding dependency map are shown here. As the diagram shows, this solution provides identical functionality as the previous solution, but no longer with circular dependencies since all the Xs are in the triangle. Of course, with this simple example we can see whether we have a circular dependency in the diagram, but imagine how difficult that would be in a complex application with hundreds of classes along with method and procedure calls. With this tool, we can recognize circular dependencies immediately even in very large applications.
When we reverse engineer the original design, we get this map showing the circular dependency between Main and GUI that we discussed earlier.
Development benefits result from this approach include:
- Elimination of “spaghetti” code through the identification of a logical structure to define classes
- Identification of the sequence to write objects for the shortest development time
- Mapping business needs to specific code for documentation
- Analyzing existing code to help with enhancements and maintenance