Working with Components


You organize a system into subsystems. A standalone subsystem, autonomous and modular (relative to the bigger system), is known as a component. UML components are like replaceable parts—you take one out and fit another in its place. We like components because we can replace it without having to change anything else in my system. Components make your systems more flexible, maintainable, scalable, and reusable. Components come in many shapes and sizes. Subsystems are one example of a large component. A complex class with many internal parts and external interfaces could also be a component. (Remember a component is a replaceable part.)

When you construct replaceable components (parts), be sure to carefully define the boundary of the component. You define the boundary by clearly describing the responsibilities and interfaces of the component. Such parts are easy to make because everyone knows exactly what the component should do. This improves productivity and makes the component easier to test (the testing teams know exactly how the part is supposed to work), which improves quality. Also, the more a component is reused, the more trustworthy and reliable it becomes.

For your components to be replaceable parts they must have the following criteria:

  • Hide the inner workings: The insides of a component are hidden from (and inaccessible by) objects outside the component. If you want to make a truly replaceable part, you can allow no dependencies to exist between the insides of the component and any other objects.

  • Provide interfaces: An interface describes what operations you can invoke on a component—but, not how any such operation is performed. An object outside a component uses an interface without knowing which instance of a class is being invoked. All an outside object must know about a component is that it’s using the appropriate interface (so it looks for the signature of the interface). That way the outside objects are kept in the dark about the inner workings of the component. Providing interfaces are a way of hiding the inside workings of a component from the outside. Components rely on the principles of encapsulation and information hiding. See Chapter 2 for more on these principles.

  • Make the inner parts independent: You must make sure the objects internal to the component have no knowledge of outside objects. Otherwise trying to replace the component would break the system—there would be no guarantees that the appropriate outside objects would be available to the replacement component.

  • Specify the required interfaces: Sometimes the objects inside your component must access objects on the outside. If the object on the outside has its own interfaces declared, then the objects on the inside use that common interface instead of accessing outside objects directly.

 Remember   You can think of a component as a subsystem with internal classes that work together to realize the publicly stated interfaces.

start sidebar
Logical versus physical

The experts talk about logical data models, physical models, logical views, and physical elements. You needn’t worry about all this babble. When the experts use the term “physical,” they are referring to something in the real world that all of us experience. The experts use of the word “logical” simply means conceptual.

For instance a physical table model (also known as a physical data model) describes the tables of a relational database as they physically exist in a database. The actual names of the table and its fields are used in physical table model. On the other hand, the logical data model describes the tables in a more generic or conceptual way—as entities. Entities are the concept behind the physical tables of a relational database.

The logical model for an object-oriented system consists of the conceptual pieces that make up the system: subsystems, components, and classes. The physical model consists of the real parts of a system: hardware, network connections, classes with design details specified, application code, scripts, and files.

end sidebar

Showing black boxes

 UML2   UML 2 redefines the meaning of the “component diagram.” In earlier versions of UML the component diagram defined components as the physical implementation of your software such as executable code files, dynamic link libraries, and source code files. The component diagram showed dependencies among the pieces of your software implementation and their hardware location. The experts who developed UML ran into a little problem: While they were busy defining diagrams, we object-oriented programmers were busy with our own meaning for the word component.

We needed replaceable parts for our applications. Classes were not replaceable. We needed an object-oriented part that provided not only behavior, but also interfaces independent of how the part worked. We needed better black boxes. So, during the mid-to-late 1990s, the practical development community came up with the idea of components. UML 2 catches up with developers and redefines the concept of component to bring it in line with the idea of an autonomous replaceable unit.

You show a component as a rectangle with the name of the component inside. The component has a stereotype of «component» and (optionally) a small icon in the upper-right corner that looks like a small box with a couple of tabs hanging off. (We’re not making this up. Honest.) Because a component must hide its inner workings from the outside, it often has interfaces attached to the sides of the rectangle. Each interface that a component provides to the outside world looks like a “lollipop” (a circle on a stick) in the diagram. Each interface that the component requires from some other class or component in the system looks like a half-circle on a stick.

Figure 19-5 provides a black-box example of UML notation for a component called PersistentStore. Here it has the «component» stereotype and the small component icon on the right side. Our application component, PersistentStore, provides the following three interfaces:

  • DBAccess: Another object in your system uses the DBAccess interface to open and close a database.

  • DBQuery: If an object in your system wants to query an open database, that object uses the DBQuery interface. Then, in response to the query, the PersistentStore component then stores or retrieves objects as needed from a database.

  • DBTest: Sometimes you have to test the connection between your system and an open database. An object in your system would use the DBTest interface for this purpose.

Our PersistentStore component also requires access to a relational database via a specific interface. Figure 19-5 shows this required interface with a half circle (socket) on a stick called Rdbms. The Rdbms interface must be provided by another class or component in your system. The PersistentStore connects its required interface to another component’s provided interface—and both have the name Rdbms.


Figure 19-5: Basic component with interfaces.

Figure 19-6 shows another black-box example of a component. Notice that UML doesn’t make you use the circles-and-half-circles-on-a-stick notation; you can replace the lollipops with operations—showing the provided interfaces with the «provided interface» stereotype and the required interfaces with the «required interface» stereotype. The diagrams shown in Figures 19-5 and 19-6 have the same meaning.


Figure 19-6: A component as a black box.

 Remember   Your components are, in effect, black boxes. Nobody can see what goes on inside them—but everyone can see their interfaces. The software components represented in your diagram have interfaces too, no less than the pieces of electronic equipment that have tangible interfaces for hooking up various cables.

Describing the interfaces

You show components as black boxes when you want to wire them together to make up your system. In the example of the PersistentStore component in the previous section, you connect the PersistentStore to another component that provides the Rdbms interface.

But, if you’re building the insides of a component for others to assemble into their system, you have to show the interfaces’ details. If (for example) other developers want to use your PersistentStore component to retrieve data from the database, they have to know the signature of the retrieve operation in the DBQuery interface, which may look like this:

 retrieve(type : Object, search : String): Object(idl) 

When you build a component, give the users of your component a special interface specification using a component diagram. In this type of diagram you show the component as a black box and the interfaces as classes. Each interface has the «interface» stereotype, the name of the interface as the name of the class, and the full operation signature for each operation with in the interface. Connect up the provided interfaces to the component with a realizes dependency. The realizes dependency shows that the component implements the operations specified by the interface. Connect the required interfaces to the component with a uses dependency. The uses dependency shows that the component must use some other component that implements that interface.

Your users of the PersistentStore component will appreciate the component diagram shown in Figure 19-7. This diagram shows users of the PersistentStore component that if they want to store an object instance in the database, they must invoke the DBQuery interface with store(theObject: Object(idl)): Object(idl). Further, if they want to perform an SQL query on the database, they would use the DBQuery interface with sqlFetch(sqlString : String) : String. To keep the diagram simple, we haven’t shown the detailed signature of operations in the DBAccess, DBTest, and Rdbms interface classes.

If you refer to Figure 19-6, you can see that it shows the PersistentStore component with three provided interfaces and one required interface. Figure 19-7 shows the same thing, only it uses dependency arrows instead of stereotypes:


Figure 19-7: A component with explicit interface specifications.

  • The dashed line with a large, closed arrowhead is the realizes dependency. That means an interface is realized by a component. For example the DBQuery interface is realized by the PersistentStore component.

  • A dashed line that has a regular arrowhead and the «uses» stereotype means that the component uses the interface—in fact, that it requires the interface. For example, the PersistentStore component uses (requires) the Rdbms interface.

Looking inside the box

But wait a minute—you want to build components, not just assemble them. You need a way of showing the insides of your component. That’s easy: just add a compartment below your component and put a class diagram there. Classes inside the component work together to accomplish the interfaces of the component.

Because you must show how the internal classes are hooked up to the component’s interfaces, UML provides some special terms and notation for the purpose:

  • Ports: A port is a point of interaction between the inside and the outside of your component. Provided and/or required interfaces are attached to these interaction points. You show a port as a small square on the edge of your component. By attaching interfaces to a port, you’re specifying the services that the component provides—or requires—through that port. One way to tell which port does what is to name it, putting the name next to the small square.

     Tip   Ports can be used on classes and subsystems as well as components.

  • Delegation: When a request for service comes into your component through a port, you have to show who handles that request. Do so with a link between the port and one of the internal classes (or components) inside the larger component. Your connecting link should be a line with an arrowhead indicating the direction of the request. The line is also stereotyped «delegate».

  • Stereotypes for inner workings: UML provides you with several stereotypes that help distinguish between the different parts inside your component. You can use the following stereotypes on the inner workings (classes and internal components) inside your component:

    • «focus»: A part with this stereotype executes some or all the business logic internal to the component.

    • «process»: A part with this stereotype executes a transaction. It must make sure that an important sequence of behavior—the transaction—completes. If the transaction fails to reach completion, this part must undo any behavior done to make the transaction happen—in effect, eating the evidence.

    • «service»: A service part has no states; it just computes a value. Such a part is really a function (sequential set of instructions) dressed up as an object.

    • «entity»: A part that persists. An entity’s attribute values, behavior, and state carry on beyond the life of the application runtime environment.

    • «auxiliary»:A part that assists the focus part with implementing business logic for the component.

  • Ball and socket: You can use the ball-and-socket notation to show assemblies inside your component. If you have one class that must have a particular interface and another class that provides that interface, then you can hook them up in the diagram: Just place the ball end of the provided interface into the half-open end of the required interface.

 Tip   Whenever you design a component, create a component diagram to show its inner workings. You use such a diagram to help you explore, design, and document the best ways to wire your component. You should also create a component diagram that shows the component as a black box surrounded by interface classes, each with a detailed operation signature. Pass this second diagram out to all the developers who will be integrating your component into their system.

Figure 19-8 provides an inner structure example of the PersistentStore component. When an object outside of the PersistentStore invokes the DBAccess interface using the openDB or closeDB operation, the request is delegated to an instance of the DBManager class.


Figure 19-8: Component diagram showing internal classes.

Incidentally, the DBManager is the focus of PersistentStore—so it makes sure any business logic for the component is handled properly. The DBManager creates an instance of the checkConnection class so the component can provide the service associated with the DBTest interface. Both the DBManager and the checkConnection must have interfaces on an internal component called Connection.

You can see the use of the ball-and-socket notation in the example. The DBManager requires the connect interface that the Connection component provides. The Connection component, DBManager and Query all require the Rdbms interface. The Rdbms interface is external to the component; it must be provided by some other class or component in the system.




UML 2 for Dummies
UML 2 For Dummies
ISBN: 0764526146
EAN: 2147483647
Year: 2006
Pages: 193

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net