Chapter 13. Organizing Your Model: Packages

As a software program grows in complexity, it can easily contain hundreds of classes. If you’re a programmer working with such a class library, how do you make sense of it? One way to impose structure is by organizing your classes into logically related groups. Classes concerned with an application’s user interface can belong to one group, and utility classes can belong to another.

In UML, groups of classes are modeled with packages . Most object-oriented languages have an analog of UML packages to organize and avoid name collision among classes. For example, Java has packages, C# has namespaces (although Java packages, and C# namespaces differ significantly in other details). You can use UML packages to model these structures.

Package diagrams are often used to view dependencies among packages. Since a package can break if another package on which it depends changes, understanding dependencies between packages is vital to the stability of your software.

Packages can organize almost any UML element—not just classes. For example, packages are also commonly used to group use cases. Package diagrams form part of the development view, which is concerned with how your system’s parts are organized into modules and packages, as shown in Figure 13-1.

The Development View describes how your system’s parts are organized into modules, which are represented as packages in UML
Figure 13-1. The Development View describes how your system’s parts are organized into modules, which are represented as packages in UML

Packages

Suppose that during the design of a CMS, you decide to keep classes related to security (for example, performing user authentication) grouped together. Figure 13-2 shows the security package and a few other packages from the CMS in UML. The symbol for a package is a folder with a tab. The name of the package is written inside the folder.

Packages in a CMS; each package corresponds to a specific system concern
Figure 13-2. Packages in a CMS; each package corresponds to a specific system concern

Contents of a Package

Packages organize UML elements, such as classes, and the contents of a package can be drawn inside the package or outside the package attached by a line, as shown in Figure 13-3. If you draw the elements inside the package, write the name of the package in the folder tab.

The notation shown in Figure 13-3 is used to model Java classes belonging to a Java package. In Java, the package keyword at the beginning of a class specifies that a class is in a package. Example 13-1 shows a Java code sample corresponding to the Credentials class in Figure 13-3.

Two ways to show that the Credentials and IdentityVerifier classes are contained in the security package
Figure 13-3. Two ways to show that the Credentials and IdentityVerifier classes are contained in the security package
Example 13-1. The Credentials class is located in the security package in this Java implementation
package security;
 
public class Credentials {
    ...
}

Packages can also contain other packages, as shown in Figure 13-4.

A package that contains another package
Figure 13-4. A package that contains another package

It’s common to see deeply nested packages in enterprise applications. Java applications typically use the URL-in-reverse package naming convention (omitting the www part of the URL). For example, the ACME company with the URL http://www.acme.com would put all its packages under the acme package, which is under com, as shown in Figure 13-5.

Deeply nested packages are common in enterprise applications: the search and indexing packages are shown in a typical package structure for the ACME company
Figure 13-5. Deeply nested packages are common in enterprise applications: the search and indexing packages are shown in a typical package structure for the ACME company

Even at this point, these packages consume a lot of space, and if you want to show classes inside the indexing package, each package containing it would have to expand in size accordingly. Luckily, there’s an alternate notation that can be easier to work with. You can “flatten” nested packages to write them as packageA::packageB::packageC, and so on. This converts Figure 13-5 into the less cluttered Figure 13-6.

Flattening nested packages
Figure 13-6. Flattening nested packages

UML Tool Variation

Currently, a small amount of UML tools don’t support the notations shown in Figure 13-3. However, almost all tools can show that a class belongs to a package using one of the notations shown in Figure 13-7. The notation to the far right is the standard UML namespace notation, discussed next in “Namespaces and Classes Referring to Each Other.”

Common ways UML tools show that a class belongs to a package
Figure 13-7. Common ways UML tools show that a class belongs to a package

To specify the package that a class belongs to, most UML tools allow you to enter the package name in a class specification dialog or manually drag the class into the package it belongs to in a tree display of the model elements.

Namespaces and Classes Referring to Each Other

Breaking up your classes into packages introduces some bookkeeping. If you’re a Java programmer, you may have encountered a related issue before. To use an ArrayList in a Java program, you have to specify that ArrayList is located in the java.util package. That is because Java packages define their own namespaces, or naming contexts. If an item is not in the current namespace, you have to specify where it is located.

Similarly, a UML package establishes a namespace. So, if an element in a package wants to use an element in a different package, it has to specify where the desired element is. To specify the context of a UML element, you provide the fully-scoped name, which includes the package name and the element name separated by double colons, as in packageName :: className. The fully-scoped name for the class Credentials belonging to the package security is security::Credentials. If you have two classes with the same name in different packages, using the fully-scoped name allows you to distinguish between them.

Elements in a namespace must have unique names. This means the security package cannot have two classes named Credentials, but there can be two classes called Credentials belonging to separate packages, for example security and utils. As discussed previously in “UML Tool Variation,” your UML tool may display the classes in Figure 13-8 differently.

Representing a class with its fully-scoped name: both the security and utils packages have a class named Credentials
Figure 13-8. Representing a class with its fully-scoped name: both the security and utils packages have a class named Credentials

Why does this matter? To specify that a class has a relationship with another class, you may have to specify a namespace.

Classes in the same package are part of the same namespace, so they can refer to each other without using fully-scoped names. Since they are in the same package, IdentityVerifier can have an attribute of type Credentials and not have to specify the package (see Figure 13-9).

Classes in different packages have to provide name scope
Figure 13-9. Classes in different packages have to provide name scope

On the other hand, a class outside the security package, such as User, would have to provide a scope when accessing Credentials, which it can do by using the fully-scoped name—security::Credentials. Later, in "Importing and Accessing Packages,” you’ll see that there are other ways to provide scope when accessing a class in a different package.

Tip

In Java, a fully-scoped name corresponds to specifying the Java package, e.g., security.Credentials instead of just Credentials.

In UML, elements in a nested package can refer to elements in the containing package without scoping the name, which in Figure 13-10 means that an element in indexing could refer to an element in search without using the fully-scoped name.

In UML, a nested package implies namespace “inheritance,” which doesn’t apply in some implementation languages
Figure 13-10. In UML, a nested package implies namespace “inheritance,” which doesn’t apply in some implementation languages

The implication that elements in nested packages have automatic access to elements in containing packages doesn’t match with some implementation languages. For example, in Java, if a class in the indexing package uses a class in the search package, it has to provide a scope either by using its fully-qualified name or by importing the search package. Despite the fact that UML semantics of nested packages differ from Java packages, you could still use Figure 13-10 to model a package search.indexing in a Java system.

Element Visibility

Elements in a package may have public or private visibility . Elements with public visibility are accessible outside the package. Elements with private visibility are available only to other elements inside the package. You can model public or private visibility in UML by writing a plus or minus symbol in front of the element’s name, as shown in Figure 13-11.

Since MD5Crypt has private visibility, it isn’t accessible outside the security package
Figure 13-11. Since MD5Crypt has private visibility, it isn’t accessible outside the security package

In Java, public and private visibility corresponds to a class being public or private to a Java package. A Java class is marked as public to a package by the public access modifier, as in:

public class Credentials {}

If the public keyword is absent, then the class is private to the package. Many UML tools don’t offer the plus and minus symbols to show element visibility, so don’t be surprised if yours doesn’t.

Package Dependency

The previous sections showed that sometimes a class in one package needs to use a class in another package. This causes a dependency between packages : if an element in package A uses an element in package B, then package A depends on package B, as shown in Figure 13-12.

Package A depends on package B
Figure 13-12. Package A depends on package B

Understanding the dependencies among your packages is useful for analyzing the stability of your software, as discussed in “Managing Package Dependencies.” In fact, the most common use of UML package diagrams is to give an overview of the core packages in your software and the dependencies among them, as shown in Figure 13-13.

A typical package diagram featuring core packages and dependencies
Figure 13-13. A typical package diagram featuring core packages and dependencies

"Managing Package Dependencies,” later in this chapter, revisits package dependency diagrams, showing you how to use them to understand and improve the stability of your software.

Importing and Accessing Packages

When a package imports another package, elements in the importing package can use elements in the imported package without having to use their fully scoped names. This feature is similar to a Java import, in which a class can import a package and use its contents without having to provide their package names.

In an import relationship, the imported package is referred to as the target package . To show the import relation, draw a dependency arrow from the importing package to the target package with the stereotype import (see Figure 13-14).

The package users imports security, so classes in users may use public classes in security without having to specify the package name
Figure 13-14. The package users imports security, so classes in users may use public classes in security without having to specify the package name

A package can also import a specific element in another package instead of the whole package, as shown in Figure 13-15.

The users package imports only the Credentials element from the security package
Figure 13-15. The users package imports only the Credentials element from the security package

When importing a package, only public elements of the target package are available in the importing namespace. For example, in Figure 13-16, elements in users can see Credentials and IdentityVerifier but not MD5Crypt.

Private visibility causes a class not to be seen even though its package is imported
Figure 13-16. Private visibility causes a class not to be seen even though its package is imported

Not only do elements have visibility—the import relation itself has visibility. An import can be a public import or private import with public as the default. A public import means imported elements have public visibility inside the importing namespace; a private import means imported elements have private visibility inside the importing namespace. You show a private import with the stereotype access instead of import.

The difference between import and access arises when a package imports a package that imports or accesses others. Imported elements have public visibility in the importing package, so they get passed on with further imports, whereas accessed elements do not.

In Figure 13-17, package B imports C and accesses D, so B can see public elements in C and D. A imports B, so A can see public elements in B. A can also see public elements in C because C is publicly imported into B, but A cannot see anything in D because D is privately imported into B.

Package A can see public elements in C but not D
Figure 13-17. Package A can see public elements in C but not D

Import and access relationships can be used to model the programming world concepts of importing of classes into another namespace so that elements in the importing namespace may refer to elements in the target namespace without scoping the name. For example, the package relationships in Figure 13-14 could be used to model the Java code example in Example 13-2.

Example 13-2. Because the User class imports the security package, it can refer to the Credentials class without using the fully qualified name security
package users;
 
// importing all public elements in the security package
import security.*;
 
class User {
    Credentials credentials;
    ...
}

The element import in Figure 13-15 corresponds to the Java implementation shown in Example 13-3.

Example 13-3. Only the Credentials class is imported from the security package
package users;
 
// importing only the Credentials class
import security.Credentials;
 
class User {
    Credentials credentials;
 
    ...
}

Many modelers don’t bother with specifying the import and access relationships, and instead show generic package dependencies, discussed earlier in "Package Dependency.”

Managing Package Dependencies

Having complicated dependencies among packages can lead to brittle software since changes in one package can cause its dependent packages to break. Figure 13-18 shows a dependency disaster: a change in any one package could ultimately affect every other package.

Directly or indirectly, a change in any one package could affect every other package
Figure 13-18. Directly or indirectly, a change in any one package could affect every other package

Robert C. Martin’s Agile Software Development (Prentice Hall) establishes principles and metrics regarding dependencies between packages and deployment modules. A couple of these, such as avoiding cyclical package dependencies and depending in the “direction of stability,” can be investigated by looking at package diagrams.

If you have cycles in your dependencies, you can break the cycles in different ways. You could factor out a new package that both packages can depend on or you could decide that all the classes really belong together anyway, as shown in Figure 13-19.

Removing cycles in package dependencies
Figure 13-19. Removing cycles in package dependencies

Depending in the order of stability means that a package should depend only on packages more stable than itself. An unstable package depends on many other packages; a stable package depends on few packages. Studying package diagrams can help you spot potentially vulnerable designs resulting from the core packages of your system (such as those containing interfaces) depending on unstable packages.

Using Packages to Organize Use Cases

Just as packages group classes of similar functionality, packages also group other UML elements such as use cases. Figure 13-20 shows some use case packages from a CMS.

Rolling up use cases into higher levels of your system can help organize your model, allowing you to see which actors interact with which portions of the system, as shown in Figure 13-21.

Packaging major use case groups within a CMS
Figure 13-20. Packaging major use case groups within a CMS
Packages enable a higher level view of how actors interact with the system
Figure 13-21. Packages enable a higher level view of how actors interact with the system

What’s Next?

Packages are used to group UML elements such as classes and use cases. You may want to review those chapters for more detail about showing the contents of a package. Class diagrams are covered in Chapter 4; use case diagrams are covered in Chapter 2.

One of the most important applications of package diagrams is to view dependencies in your system. Other important high-level system diagrams include component diagrams, which show the key software pieces, and deployment diagrams, which show how the pieces get deployed to hardware. Component diagrams are described in Chapter 12; deployment diagrams are covered in Chapter 15.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.111.125