Software Engineering: A Modern Approach
1 Modules Should Be Deep
1.1 Introduction
John Ousterhout is a professor at Stanford University and the creator of the Tcl/Tk scripting language. In 2018, he published a book on software design principles.
One of his main recommendations—and probably one of the most original compared to other literature on software design—is the following: the modules of a system should be deep.
Before explaining the concept of deep,
let’s first define a
module. In software design, a module is any code element that has both
an implementation and an interface.
Modules = Interface + Implementation
Modules can vary in size and include functions, classes, and packages. For functions, the interface is defined by their signature.
1.2 Deep Modules
Returning to Ousterhout’s advice, a module is considered deep when its implementation is significantly more complex than its interface. In other words, modules should offer their clients a simple interface while providing a complex implementation behind it.
On the other hand, a shallow module has an interface that is nearly as complex as its implementation. To better understand this concept, consider the following illustrations:
As an example of a deep module, the author mentions the Unix file system, whose interface consists of only five functions:
int open(const char* path, int flags, mode_t permissions);
ssize_t read(int fd, void* buffer, size_t count);
ssize_t write(int fd, const void* buffer, size_t count);
off_t lseek(int fd, off_t offset, int referencePosition);
int close(int fd);
The implementation of a file system in any operating system is quite complex, as it involves dealing with disk devices, maintaining data structures to store files, managing permissions, supporting concurrent access to files, handling caches, and other intricate tasks. However, in the Unix case, this complexity is hidden behind a very simple interface (the five functions shown above). Importantly, Ousterhout notes that the design of Unix file systems has radically evolved over the years, but this evolution has been transparent to users because the signatures of the five functions have remained unchanged.
The author mentions a second example of a deep module: garbage collectors. Despite their complexity, garbage collection algorithms are encapsulated in modules that have virtually no visible interface to most programmers.
In my lectures, I often use an alternative metaphor proposed in a book by Prof. Bertrand Meyer. He suggests that modules should be like icebergs, with a small tip (the interface) and a large base (the implementation), which is submerged and invisible to external observers. The figure below, used in Meyer’s book, illustrates this concept:
1.3 Shallow Modules
On the other hand, a shallow module has an interface that is relatively complex compared to its implementation. As an extreme example, the author presents the following function:
void addNullValueForAttribute(String attribute) { // interface
data.put(attribute, null); // implementation
}
In this case, it is simpler and easier to directly call:
data.put(attribute, null);
than to implement a function solely for this purpose. The function
addNullValueForAttribute
adds complexity to the system
without providing significant benefits.
Exercises
1. Give two examples of deep modules. To facilitate your answer, consider packages or libraries in your preferred programming language.
2. Consider the following statement: deep modules necessarily imply large functions or classes with many lines of code. Is this statement true or false? Justify and discuss your answer.
3. Suppose the following method of the String
class in
Java:
public boolean isEmpty() {
return value.length == 0;
}
Is this method shallow? Justify and discuss.
Suggestion: To answer the question think about the code that calls
isEmpty()
. For instance, would you prefer the following
code
if (myString.isEmpty()) {
...
}
over code like this:
if (myString.value.length() == 0) {
...
}
4. Consider the following statement: every shallow module is small, but not every small module is shallow. Is this statement true or false? Justify and discuss. (Note: A small module is one that has a few lines of code.)
5. Consider the following statement taken from Martin Fowler’s Refactoring book (page 90):
Small methods really work only when you have good names, so you need to pay attention to naming. People sometimes ask me what length I look for in a method. To me length is not the issue. The key is the semantic distance between the method name and the method body.
Explain the concept of semantic distance between the method name and the method body, as mentioned in this quotation.
Check out the other articles on our site.