In this chapter
Classes and artifacts
Static models and dynamic models
Connections among models
Extending the UML
Brian Kernighan and Dennis Ritchie, the authors of the C programming language, point out that “the only way to learn a new programming language is by writing programs in it.” The same is true of the UML. The only way to learn the UML is by writing models in it.
The first program many developers write when approaching a new programming language is a simple one, involving little more than printing the string “Hello, World!” This is a reasonable starting point, because mastering this trivial application provides some instant gratification. It also covers all the infrastructure needed to get something running.
This is where we begin with the UML. Modeling “Hello, World!” is about the simplest use of the UML you'll ever find. However, this application is deceptively easy because underneath it all there are some interesting mechanisms that make it work. These mechanisms can easily be modeled with the UML, providing a richer view of this simple application.
In Java, the applet for printing “Hello, World!” in a Web browser is quite simple:
import java.awt.Graphics; class HelloWorld extends java.applet.Applet { public void paint (Graphics g) { g.drawString("Hello, World!", 10, 10); } }
The first line of code:
import java.awt.Graphics;
makes the class Graphics
directly available to the code that follows. The java.awt
prefix specifies the Java package in which the class Graphics
lives.
The second line of code:
class HelloWorld extends java.applet.Applet {
introduces a new class named HelloWorld
and specifies that it is a kind of class just like Applet
, which lives in the package java.applet
.
The next three lines of code:
public void paint (Graphics g) { g.drawString("Hello, World!", 10, 10); }
declare an operation named paint
, whose implementation invokes another operation, named drawString
, responsible for printing the string "Hello, World!"
at the given coordinates. In the usual object-oriented fashion, drawString
is an operation on a parameter named g
, whose type is the class Graphics
.
Modeling this application in the UML is straightforward. As Figure 3-1 shows, you can represent the class HelloWorld
graphically as a rectangular icon. Its paint
operation is shown here as well, with all its formal parameters elided and its implementation specified in the attached note.
The UML is not a visual programming language, although, as the figure shows, the UML does allow—but does not require—a tight coupling to a variety of programming languages, such as Java. The UML is designed to allow models to be transformed into code and to allow code to be reengineered back into models. Some things are best written in the syntax of a textual programming language (for example, mathematical expressions), whereas other things are best visualized graphically in the UML (for example, hierarchies of classes).
This class diagram captures the basics of the “Hello, World!” application, but it leaves out a number of things. As the preceding code specifies, two other classes—Applet
and Graphics
—are involved in this application and each is used in a different way. The class Applet
is used as the parent of HelloWorld
, and the class Graphics
is used in the signature and implementation of one of its operations, paint
. You can represent these classes and their different relationships to the class HelloWorld
in a class diagram, as shown in Figure 3-2.
The Applet
and Graphics
classes are represented graphically as rectangular icons. No operations are shown for either of them, so their icons are elided. The directed line with the hollow arrowhead from HelloWorld
to Applet
represents generalization, which in this case means that HelloWorld
is a child of Applet
. The dashed directed line from HelloWorld
to Graphics
represents a dependency relationship, which means that HelloWorld
uses Graphics
.
This is not the end of the framework upon which HelloWorld
is built. If you study the Java libraries for Applet
and Graphics
, you will discover that both of these classes are part of a larger hierarchy. Tracing just the classes that Applet
extends and implements, you can generate another class diagram, shown in Figure 3-3.
This figure is a good example of a diagram generated by reverse engineering an existing system. Reverse engineering is the creation of a model from code.
This figure makes it clear that HelloWorld
is just a leaf in a larger hierarchy of classes. HelloWorld
is a child of Applet
; Applet
is a child of Panel
; Panel
is a child of Container
; Container
is a child of Component
; and Component
is a child of Object
, which is the parent class of every class in Java. This model thus matches the Java library—each child extends some parent.
The relationship between ImageObserver
and Component
is a bit different, and the class diagram reflects this difference. In the Java library, ImageObserver
is an interface, which, among other things, means that it has no implementation and instead requires that other classes implement it. You can show that class Component
implements interface ImageObserver
by the solid line from the rectangle (Component
) to a provided interface circle (ImageObserver
).
As these figures show, HelloWorld
collaborates directly with only two classes (Applet
and Graphics
), and these two classes are but a small part of the larger library of predefined Java classes. To manage this large collection, Java organizes its interfaces and classes in a number of different packages. The root package in the Java environment is named, not surprisingly, java
. Nested inside this package are several other packages, which contain other packages, interfaces, and classes. Object
lives in the package lang
, so its full path name is java.lang.Object
. Similarly, Panel
, Container
, and Component
live in awt
; the class Applet
lives in the package applet
. The interface ImageObserver
lives in the package image
, which in turn lives in the package awt
, so its full path name is the rather lengthy string java.awt.image.ImageObserver
.
You can visualize this packaging in a class diagram, shown in Figure 3-4. As this figure shows, packages are represented in the UML as a tabbed folders. Packages may be nested, and the dashed directed lines represent dependencies among these packages. For example, HelloWorld
depends on the package java.applet
, and java.applet
depends on the package java.awt
.
The hardest part of mastering a library as rich as Java's is learning how its parts work together. For example, how does HelloWorld
's paint
operation get invoked? What operations must you use if you want to change the behavior of this applet, such as making it print the string in a different color? To answer these and other questions, you must have a conceptual model of the way these classes work together dynamically.
Studying the Java library reveals that HelloWorld
's paint
operation is inherited from Component
. This still begs the question of how this operation is invoked. The answer is that paint
is called as part of running the thread that encloses the applet, as Figure 3-5 illustrates.
This figure shows the collaboration of several objects, including one instance of the class HelloWorld
. The other objects are a part of the Java environment, so, for the most part, they live in the background of the applets you create. This shows a collaboration among objects that can be applied many times. Each column shows a role in the collaboration, that is, a part that can be played by a different object in each execution. In the UML, roles are represented just like classes, except they have both role names and types. The middle two roles in this diagram are anonymous, because their types are enough to identify them within the collaboration (but the colon and the absence of an underline mark them as roles). The initial Thread
is called root
, and the HelloWorld
role has the name target
known by the ComponentPeer
role.
You can model the ordering of events using a sequence diagram, as in Figure 3-5. Here, the sequence begins by running the Thread
object, which in turn calls the Toolkit
's run
operation. The Toolkit
object then calls one of its own operations (callbackLoop
), which then calls the ComponentPeer
's handleExpose
operation. The ComponentPeer
object then calls its target's paint
operation. The ComponentPeer
object assumes that its target is a Component
, but in this case the target is actually a child of Component
(namely, HelloWorld
), so HelloWorld
's paint
operation is dispatched polymorphically.
“Hello, World!” is implemented as an applet, so it never stands alone but instead is typically a part of some Web page. The applet starts when its enclosing page is opened, triggered by some browser mechanism that runs the applet's Thread
object. However, it's not the HelloWorld
class that's directly a part of the Web page. Rather, it's a binary form of the class, created by a Java compiler that transforms the source code representing that class into an artifact that can be executed. This suggests a very different perspective of the system. Whereas all the earlier diagrams represented a logical view of the applet, what's going on here is a view of the applet's physical artifacts.
You can model this physical view using an artifact diagram, as in Figure 3-6.
The logical class HelloWorld
is shown at the top as a class rectangle. Each of the other icons in this figure represents a UML artifact in the implementation view of the system. An artifact is a physical representation, such as a file. The artifact called hello.java
represents the source code for the logical class HelloWorld
, so it is a file that may be manipulated by development environments and configuration management tools. This source code can be transformed into the binary applet hello.class
by a Java compiler, making it suitable for execution by a computer's Java virtual machine. Both the source code and the binary applet manifest—physically implement—the logical class. This is shown by the dashed arrows with the keyword «manifest»
.
The icon for an artifact is a rectangle with the keyword «artifact»
above the name. The binary applet HelloWorld.class
is a variation of this basic symbol, with its lines made thicker, indicating that it is an executable artifact (just like an active class). The icon for the hello.java
artifact has been replaced with a user-defined icon, representing a text file. The icon for the Web page hello.html
has been similarly tailored by extending the UML's notation. As the figure indicates, this Web page has another artifact, hello.jpg
, which is represented by a user-defined artifact icon, in this case providing a thumbnail sketch of the graphics image. Because these latter three artifacts use user-defined graphical symbols, their names are placed outside the icon. The dependencies among the artifacts are shown by dashed arrows.
The relationships among the class (HelloWorld
), its source code (hello.java
), and its object code (HelloWorld.class
) are rarely modeled explicitly, although it is sometimes useful to do so to visualize the physical configuration of a system. On the other hand, it is common to visualize the organization of a Web-based system such as this by using artifact diagrams to model its pages and other executable artifacts.