7.5.1 Showing the Existence of Interfaces
The existence of interfaces can be shown in the primary presentations by using most graphical notations available for architecture. Figure 7.3 shows some examples using an informal notation.
Figure 7.3. Graphical notations for interfaces typically show a symbol on the boundary of the icon for an element. Lines connecting interface symbols denote that the interface exists between the connected elements. Graphical notations like this can show only the existence of an interface, not its definition. (a) An element with multiple interfaces. For elements with a single interface, the interface symbol is often omitted. (b) Multiple actors at an interface. Clients 1 and 2 both interact with Primary Server via the same interface.
The existence of an interface can be implied even without using an explicit symbol for it. If a relationship symbol joins an element symbol and the relationship type involves an interactionas opposed to, say, is a subclass of, that implies that the interaction takes place through the element's interface.
ADVICE
Use an explicit interface symbol in your primary presentations if
- Some elements have more than one interface.
- You wish to emphasize the interface for an element; for example, you are making provisions for multiple elements that realize the same interface.
Although it's never wrong to show interfaces explicitly, it is not necessary to do so if
- No element has more than one interface.
- You wish to reduce the visual clutter of the diagrams.
|
Sometimes, interfaces are depicted by themselves, without an associated element. When actors are shown interacting through this interface, it indicates that any element implementing the interface can be used. This is a useful means of expressing a particular kind of variability: the ability to substitute realizing elements, as shown in Figure 7.4(a). We say that an interface is realized by the element that implements it. Graphically, this is shown as a line resembling relationships among elements, as shown in Figure 7.4(b).
Figure 7.4. An interface can be shown separately from any element that realizes it, thus emphasizing the interchangeability of element implementations. (a) Another version of Figure 7.3(b), showing the Primary Server interacting with the interfaces of Clients 1 and 2 and Backup Server, without showing those elements. The emphasis here is on the interface. Elsewhere, the architect can show or list the possible elements that realize each interface. (b) An interface shown by itself emphasizes that many elements can realize it. If a specific set of possibilities has been identified, their candidacy can be shown graphically by using a figure like this.
Figure 7.5 illustrates how interfaces are shown in UML. Although it shows the existence of an interface, Figure 7.5 reveals little about the definition of an interface: the resources it provides or requires, or the nature of its interactions. This information must be provided in the supporting documentation that accompanies the primary presentation.
Figure 7.5. Showing syntactic information about interfaces in UML. UML uses a "lollipop" to denote an interface, which can be appended to classes and subsystems, among other things. UML also allows a class symbol, a box, to be stereotyped as an interface; the triangle dashed arrow shows that an element realizes an interface. The bottom part of the class symbol can be annotated with the interface's signature information: method names, arguments and argument types, and so on. The lollipop notation is normally used to show dependencies from elements to the interface; the box notation allows a more detailed interface description, such as the operations provided by the interface.
7.5.2 Conveying Syntactic Information
The Object Management Group (OMG) Interface Definition Language (IDL) is used in the CORBA community to specify interfaces' syntactic information. IDL provides language constructs to describe data types, operations, attributes, and exceptions. But the only language support for semantic information is a comment mechanism. An example of an IDL interface specification is given in Section 7.6.2.
Most programming languages have built-in ways to specify the signature of an element. C header (.h) files and Ada package specifications are two examples.
Finally, using the «interface» stereotype on a class in UML, as shown in Figure 7.5, provides the means for conveying some syntactic information about an interface. At a minimum, the interface is named; in addition, the architect can specify signature information.
7.5.3 Conveying Semantic Information
Natural language is the most widespread notation for conveying semantic information. Boolean algebra is often used to write down preconditions and postconditions, which provide a relatively simple and effective method for expressing semantics. Traces are also used to convey semantic information by writing down sequences of activities or interactions that describe the element's response to a specific use.
Semantic information often includes the behavior of an element or one or more of its resources. In that case, any number of notations for behavior come into play.
7.5.4 Summary
No single notation adequately documents interfaces; practitioners have to use a combination. When showing the existence of interfaces in the views' primary presentations, use the graphical notation of choice. Use one of the syntactic notations to document the syntactic portion of an interface's specification. Use natural language, Boolean algebra for pre- and postconditions, or any of the behavior languages to convey semantic information. Document patterns of usage, or protocols, as rich connectors, or show usage scenarios accompanied by examples of how to use the element's resources to carry out each scenario.
PERSPECTIVES
Multiple Interfaces
Elements having multiple interfaces raise some subtle design issues and some important documentation issues. First, if an element has more than one actor, it's usually best to show interfaces explicitly in your diagrams. If you don't, a diagram such as Figure 7.6(a) can be ambiguous: Does E have one interface or two? Showing the interface symbol, as in Figures 7.6(b) or 7.6(c), resolves the ambiguity.
Figure 7.6. (a) Does element E have one interface or two? This diagram makes it difficult to determine at a glance. (b) By using the interface symbol, it's clear that this element has one interface and that (c) this element has two interfaces.
Second, if you have an element that needs to interact with more than one actor, you have at least three choices for how to handle it (see Figure 7.7).
- Have all interactors operating via a single interface (Figure 7.7(a)). This approach compels the code in element E to handle any interactions among the actors. What, for instance, shall the element do if two actors try to access its services simultaneously? Or what happens if one actor sets up a transaction with Efor example, calls an init() method in a particular way unique to itbut before it can request the transaction it set up, the second actor carries out a setup operation?
- Have a separate interface dedicated for the use of each actor (Figure 7.7(b)).
- Have mediation handled in the connector that ties element E to its interactors (Figure 7.7(c)). Figures 7.7(a) and (b) are view neutral, but Figure 7.7(c) shows a connector that handles the mediation and so is firmly rooted in the component-and-connector world. Here, the connector is more than a relation; it is a first-class element, with computational semantics of its own.
Figure 7.7. Three ways to handle multiple actors interacting with an element. (a) Element E has a single interface through which all its actors interact with it. (b) Element E has a separate interface for each actor. (c) Element E has a single interface attached to a connector that handles the mediation among actors. These three approaches imply different documentation obligations: The effects of contention among E's actors must be documented in, respectively, the interface specification, the behavior of E or the ensemble of E and its actors, and the behavior of the connector, which is an element in its own right.
With respect to documentation, these three approaches determine where the semantics of mediation or conflict resolution should be explained. The approaches of Figures 7.7(a) and (b) both imply that the mediation among multiple actors is handled by element E. The approach of Figure 7.7(a) imposes on E's interface a documentation obligation that explains what happens when two or more actors try to access the interface simultaneously. The approach of Figure 7.7(b), on the other hand, implies that any one interface does not document mediationbecause any interaction with its actor is going to be sequentialbut that the element overall documents mediation among competing actors. The semantics of this interaction are documented in a behavioral specification of E or the ensemble consisting of E and its actors. Finally, the approach of Figure 7.7(c) requires an explanation of the mediation in the behavioral model of the connector.
This discussion assumes that the interfaces we're discussing are instances of the same interface type: that all actors have the same kind of interactions available to them with the element in question. If you have different kinds of interactions, the approach in Figure 7.7(b) wins hands down; it's best to document those as different interfaces. For one thing, it will make detailed modeling of a system's execution behavior easier because each interface may have properties that characterize the runtime state of the interaction with a particular party. A good example is a client-specific session key maintained for each party interacting with a server.
What if your implementation is going to be in a language or programming system that does not support multiple interfaces? You can still document the elements as having multiple logical interfaces in an architectural design if it interacts with its environment in different ways. Suppose that you have an element that provides a set of services or methods that may be invoked synchronously by other elements, as well as a set of events that the element may announce asynchronously to other elements. Such an element can easily be documented as having at least two interfaces: one for its service-oriented point of interaction and one for its event-oriented point of interaction.
P. C. C. and D. G.
|
COMING TO TERMS
Signature, Interface, API
Three terms people use when discussing element interactions are signature, API, and interface. Often, they use the terms interchangeably, with unfortunate consequences for their projects. We have already defined an interface to be a boundary across which two independent entities meet or communicate with each other, and we have seen that documenting an interface consists of naming and identifying it, documenting syntactic information, and documenting semantic information.
A signature deals with the syntactic part of documenting an interface. When an interface's resources are invokable procedures, each comes with a signature that names the procedure and defines its parameters. Parameters are defined by giving their order, data type, and, sometimes, whether their value is changed by the procedure. A procedure's signature is the information that you would find about it, for instance, in the element's C or C++ header file.
An API, or application programming interface, is a vaguely defined term that people use in various ways to convey interface information about an element. Sometimes, people assemble a collection of signatures and call that an element's API. Sometimes, people add statements about programs' effects or behavior and call that an API. An API for an element is usually written to serve developers who use the element.
Signatures and APIs are useful but are only part of the story. Signatures can be used, for example, to enable automatic build checking, which is accomplished by matching the signatures of different elements' expectations of an interface, often simply by linking different units of code. Signature matching will guarantee that a system will compile and/or link successfully. But it guarantees nothing about whether the system will operate successfully, which is, after all, the ultimate goal.
For a simple example, consider two elements: One provides a read() method, and the other wants to use a read() method. Let's assume that the signatures match as well. So a simple automated check would determine that the elements are syntactically compatible. But suppose that the read() method is implemented such that it removes data from its stream as read() is executed. The user, on the other hand, assumes that read() is free of side effects and hence can read and reread the same data. The semantic mismatch here will lead to errors and is why interfaces need to be specified beyond signatures.
As we have seen, a full-fledged interface is written for a variety of stakeholders, includes both requires and provides information, and specifies the full range of effects of each resource, including quality attributes. Signatures and low-end APIs are simply not enough to let an element be put to work with confidence in a system. A project that adopts them as a shortcut will pay the price when the elements are integrated, if they're lucky, but more than likely after the system has been delivered to the customer.
An analogy can be found in aviation. Every year in the United States, the Federal Aviation Administration and the National Transportation Safety Board spend millions of dollars tracking down counterfeit, low-quality aircraft parts. Jet engines, for example, are attached to aircraft by special bolts that have been engineered to have the right strength, durability, flexibility, and thermal properties. The next time you board a jet aircraft, imagine that the mechanic who reattached the jet engines after their last overhaul used whatever bolts were long enough and thick enough and happened to be lying around the parts bin. That's the mechanical engineering version of signature matching.
|