C++ Packaging and Design Rules
Presents how to organize and package component-based software in a uniform (domain-independent) manner. This chapter also provides the fundamental C++ design rules that govern how to develop modular software hierarchically in terms of components, packages, and package groups.
2.1 The Big Picture
The way in which software is organized governs the degree to which we can leverage that software to solve current and new business problems quickly and effectively. By design, much of the code that we write for use by applications will reside in sharable libraries and not directly in any one application. Our goal, therefore, is to provide some top-level organizational structure — such as the one illustrated in Figure 2-1 — that allows us to partition our software into discrete physical units so as to facilitate finding, understanding, and potentially reusing available software solutions.1
FIGURE 2-1: Enterprise-level view of software organization
As Chapters 0 and 1 describe, most of what we do with respect to creating new library and application software involves components as the atomic units of design. But components alone, as depicted in Figure 2-2a, are too small to be effective in managing and maintaining software on a large scale. We will therefore want to aggregate logically related components having similar physical dependencies into a larger physical entity that we refer to as a package, which can be treated more effectively as a unit. These larger logically and physically cohesive entities can then, in turn, be further aggregated into a yet larger body of software, which we call a package group, comprising packages having similar physical dependencies2 that, taken as a whole, are suitable for independent release, as illustrated in Figure 2-2b.
FIGURE 2-2: Individual components do not scale up.
In addition, some of the software that we might need to use could be organized quite differently. For example, we may want to take advantage of certain third-party and open-source libraries, which might not be component-based. We might have our own legacy libraries to use that are also not component-based. These software libraries, of necessity, must come together at a level of aggregation larger than components, as depicted in Figure 2-3.
FIGURE 2-3: Integration with non-component-based (library) software
We generally think of a top-level unit of integration within a large system informally as a “library” whose interface typically consists of a collection of header files in a single directory (e.g., /usr/include) and a single library archive (e.g., libc.a, libc.so) depending on the target platform. We might uniquely refer to this particular architectural entity as a whole as “The C Library” although its internal structure (i.e., how logical content is partitioned among its .o files) is entirely organizational (i.e., not part of its specification or contract; see Volume II, section 5.2) and might vary from one vendor platform to another.
Integration with legacy, open-source, and third-party libraries is important and will be addressed. Our purpose in the next few sections, however, is first to identify desirable characteristics of library software and then to provide a prescriptive methodology for packaging our own. After that, we will return to the issues of integrating with non-component-based software (see section 2.12) and then focus on the custom (nonshareable) top-level application code surrounding main() (see section 2.13).