The uses style of the module viewtype comes about when the depends-on relation is specialized to uses. An architect may employ this style to constrain the implementation of the architecture. This style tells developers what other modules must exist in order for their portion of the system to work correctly. This powerful style enables incremental development and the deployment of useful subsets of full systems.
2.2.2 Elements, Relations, and Properties
Table 2.2 summarizes the discussion of the characteristics of the uses style. The elements of this style are the modules as in the module viewtype. We define a specialization of the depends-on relation to be the uses relation, whereby one module requires the correct implementation of another module for its own correct functioning. This view makes explicit how the functionality is mapped to an implementation by showing relationships among the code-based elements: which elements use which other elements to achieve their functions.
2.2.3 What the Uses Style Is For and What It's Not For
This style is useful for planning incremental development, system extensions and subsets, debugging and testing, and gauging the effects of specific changes.
2.2.4 Notations for the Uses Style
Informally, the uses relation is conveniently documented as a matrix, with the modules listed as rows and columns. A mark in element (x,y) indicates that module x uses module y. The finest-grained modules in the decomposition hierarchy should be the ones listed, as fine-grained information is needed to produce incremental subsets.
|Elements||Module as defined by the module viewtype.|
|Relations||The uses relation, which is a refined form of the depends-on relation. Module A uses module B if A depends on the presence of a correctly functioning B in order to satisfy its own requirements.|
|Properties of elements||As defined by the module viewtype.|
|Properties of relations||The uses relation may have a property that describes in more detail what kind of uses one module makes of another.|
|Topology||The uses style has no topological constraints. However, if loops in the relation contain many elements, the ability of the architecture to be delivered in incremental subsets will be impaired.|
The uses relation can also be documented as a two-column table, with using elements on the left and the elements they use listed on the right. Alternatively, informal graphical notations can show the relation by using the usual box-and-line diagram with a key. For defining subsets, a tabularthat is, nongraphicalnotation is preferred. It is easier to look up the detailed relations in a table than to find them in a diagram, which will rapidly grow too cluttered to be useful except in trivial cases.
The uses style is easily represented in UML. The UML subsystem construct (see the graphic on page 64) can be used to represent modules; the uses relation is depicted as a dependency with the stereotype <>. In Figure 2.3(a), the User Interface module is an aggregate module with a uses dependency on the DataBase module. When a module is an aggregate, the decomposition requires that any uses relation involving the aggregate module be mapped to a submodule using that relation. In Figure 2.3(b), the User Interface module is decomposed into modules A, B, and C. At least one of the modules must depend on the DataBase module; otherwise, the decomposition is not consistent.
Figure 2.3. (a) The User Interface module is an aggregate module with a uses dependency on the DataBase module. We use UML Package notation to represent modules and the specialized form of depends-on arrow to indicate a uses relation. (b) Here is a variation of Figure 2.3(a) in which the User Interface module has been decomposed into modules A, B, and C. At least one of the modules must depend on the DataBase module or the decomposition would not be consistent.
The convention for showing interfaces explicitly and separate from elements that realize them can also be shown in a uses view. In Figure 2.4, the DataBase module has two interfaces, which are used by the User Interface and the Administrative System modules, respectively.
Figure 2.4. UML can be used to represent the uses view and show interfaces explicitly. Here, the DataBase module has two interfaces, which are used by the User Interface and the Administrative System modules, respectively. The lollipop notation for interfaces would also work well here.
2.2.5 Relation to Other Styles
The uses style also goes hand in hand with the layered style, with the allowed-to-use relation governing. An allowed-to-use relation usually comes first and contains coarse-grained directives defining the degrees of freedom for implementers. Once implementation choices have been made, the uses view emerges and governs the production of incremental subsets.
2.2.6 Example of the Uses Style
The following, taken from Appendix A, is a small excerpt of a uses view's primary presentation. The notation is textual, using the two-column format described earlier. Like most primary presentations, this one names only the elements; they are defined in the view's supporting documentation (not shown here).
|SDPS Element||Uses This Element|
|Science Data Processing Segment|
|INGST CSCI||ADSRV CSCI in the Interoperability Subsystem|
|STMGT CSCI in the Data Server Subsystem|
|SDSRV CSCI in the Data Server Subsystem|
|DCCI CSCI in the Communications Subsystem|
|[etc.]||other CSCIs within the Ingest Subsystem|
|Data Server Subsystem|
|DDIST CSCI||MCI CSCI in the System Management Subsystem|
|DCCI CSCI in the Communications Subsystem|
|STMGT CSCI in the Data Server Subsystem|
|INGST CSCI in the Ingest Subsystem|
|[etc.]||other CSCIs within the Data Server Subsystem|
|[etc.]||other subsystems within the Science Data Processing Segment|
COMING TO TERMS
Two of the module viewtype styles that we present in this bookthe uses style and the layered styleare based on one of the most underutilized relations in software engineering: uses. The uses relation is a special form of the depends-on relation. A unit of software P1 is said to use another unit P2 if P1's correctness depends on a correct implementation of P2 being present.
The uses relation resembles, but is decidedly not, the simple calls relation provided by most programming languages. Here's why.
So "uses" is not "calls" or "invokes." Likewise, "uses" is different from other depends-on relations, such as includes or inherits from. The includes relation deals with compilation dependencies but need not influence runtime correctness. The inherits-from relation is also usually a preruntime dependency not necessarily related to uses.
The uses relation imparts a powerful capability to a development team: It enables the building of small subsets of a total system. Early in the project, this allows incremental development, a development paradigm that allows early prototyping, early integration, and early testing. At every step along the way, the system carries out part of its total functionality, even if far from everything, and does it correctly. Fred Brooks (1995) writes about the "electrifying effect" on team morale that is caused by seeing the system first succeed at doing something. Absent incremental development, nothing works until everything works, and we are reduced to the somewhat eschewed waterfall model of development. Subsets of the total system are also useful beyond development. They provide a safe fallback in the event of slipped schedules: It is much better for the project manager to offer the customer a working subset of the system at delivery time rather than apologies and promises. And a subset of the total system can often be sold and marketed as a downscaled product in its own right.
Here's how it works. Choose a program that is to be in a subset; call it P1. In order for P1 to work correctly in this subset, correct implementations of the programs it uses must also be present. So include them in the subset. For them to work correctly, their used programs must also be present, and so forth. The subset consists of the transitive closure of P1's uses. Conceptually, you pluck P1 out from the uses graph and then see what programs come dangling beneath it. There's your subset.
The most well-behaved uses relation forms a hierarchy: a tree structure. Subsets are then defined by snipping off subtrees. But architecture is seldom that simple, and the uses relation most often forms a nontree graph. Loops in the relationthat is, for example, where P1 uses P2, P2 uses P3, and P3 uses P1are the enemy of simple subsets. A large uses loop necessitates bringing in a large number of programsevery member of the loopinto any subset joined by any member. "Bringing in a program" means, of course, that it must be implemented, debugged, integrated, and tested. But the point of incremental development is that you'd like to bring in a small number of programs to each new increment, and you'd like to be able to choose which ones you bring in and not have them choose themselves.
A technique for breaking loops in the uses relation is called sandwiching. It works by finding a program in the loop whose functionality is a good candidate for splitting in half. Say that program P4 is in a uses loop. We break program P4 into two new programs, P4A and P4B. We implement them in such a way so that
This breaks the loop:
In the figure on the left, if any element is a member of a subset, they all must be. In the figure on the right, P4 has been divided into two parts that do not use each other directly. Now, including, say, P2 in a subset necessitates inclusion of only a small number of additional elements. If P4B is desired in a subset, the situation is as before. But we could use sandwiching again on, say, P6 to shorten the uses chain.
Besides managing subsets, the uses relation is also a useful tool for debugging and integration testing. If you discover a program that's producing incorrect results, the problem is going to be either in the program itself or in the programs that it uses. The uses relation lets you instantly narrow the list of suspects. In a similar way, you can employ the relation to help you gauge the effects of proposed changes. If a program's external behavior changes as the result of a planned modification, you can backtrack through the uses relation to see what other programs may be affected by that modification.
As originally defined by Parnas (1979), the uses relation was a relation among "programs" or other relatively fine-grained units of functionality. The modern analog would be methods of an object. Parnas wrote that the uses relation was "conveniently documented as a binary matrix," where element (i,j) is true if and only if program i uses program j.
As with many relations, there are shorthand forms for documenting uses; a complete enumeration of the ordered pairs is not necessary. For example, if a group of programs made up of module A use another group of programs made up of module B, we can say that module A uses module B. We can also say A uses B if some programs in A use some programs in B, as long as you don't mind bringing in all of module B to any subset joined by any program of module A. This makes sense if module B is already implemented and ready to go, is indivisiblemaybe it's a COTS productor if its functionality is so intertwined that B cannot be teased apart. The main point about size is that the more finely grained your uses relation, the more control you have over the subsets you can field and the debugging information you can infer.
 Or perhaps it calls a program whose name was bound by a parameter at system-generation time or a program whose name it looks up via a name server. Many schemes are possible.
 Of course, calls and other depends-on relations must be given their due. If a program in the subset calls, includes, or inherits from another program but doesn't use it, the compiler is still going to expect that program to be present. But if it isn't used, there need not be a correct implementation of it: a simple stub, possibly returning a pro forma result, will do just fine.