Chapter 3. Today with Use Cases

As we established in earlier chapters, to get a truly extensible system, we must keep concerns separate all the way to code and must modularize the implementation accordingly. We also showed how this can be achieved with AOP. However, there still remains a challenge—that of finding concerns and expressing them clearly. Fortunately, we already have a well-proven technique to find and express them. It is the use-case technique. Use cases help us explore the various ways in which the system is used. They provide a means to validate stakeholder concerns early in the project. They are also excellent tools to drive the definition of the system architecture and the development and delivery of the system. However, until now, use cases have lived with a gap—we have been unable to keep use cases separate during their realization. If we can bridge this gap with aspect orientation, not only can we preserve the modularity of concerns, we also can harvest all the knowledge we have regarding use cases and apply it to aspect-oriented software development (AOSD).

Use Cases in Brief

What makes use cases more attractive than traditional ways of capturing requirements such as through feature specifications? A feature specification attempts to reply to the question, What is the system supposed to do? The use-case strategy forces us to add three words to the end of that question: “for each user?” (Actually, we mean for each user type). Each use case is the system’s behavior when interacting with a user. It considers the variations that the system must handle to meet that user’s needs effectively. Since use cases explore requirements from the user’s perspective, they are natural candidates as techniques to model and organize user and stakeholder concerns.

The concept of use cases and use-case-driven development was first introduced by Jacobson [Jacobson 1987] and since then has grown in popularity—project teams both large and small have applied it successfully. In fact, it is fast becoming the de facto standard means to elicit the needs of stakeholders and capture requirements. Along with its popularity comes a huge amount of knowledge and experience about how to apply the technique effectively.

So, what is a use case? Intuitively, a use case is a sequence of actions performed by the system to yield an observable result of value to a particular user. Formally, a use case is a class-like construct that describes a related set of usages of the system by a particular actor (user) type. In UML, actors are represented as stick figures, and use cases are represented by ellipses. Lines indicate which actors are involved in which use cases. We use the Hotel Management System (HMS) as an example. The two primary actors for the system are the customer who can reserve rooms and the hotel counter staff who can check in and check out customers (see Figure 3-1).

Use cases for Hotel Management System.

Figure 3-1. Use cases for Hotel Management System.

Each use case describes the necessary actions to perform the tasks, including the variations. For example, the Reserve Room use case collates stakeholder concerns about reserving rooms—how to deal with different types of rooms, different customer types, different reservation schemes, and so on. Success in applying use cases comes not just by being able to draw stick figures, ellipses, and arrows to present the requirements of a system. Success is attributed more to being able to systematically explore and validate the users’ concerns in terms of how the desired system is used under different scenarios and highlighting how variations are handled. This promotes a better understanding between user representatives and the development team.

The use-case model contains actors, use cases, and relationships between them. It is a kind of requirements model. As mentioned, the goal of use-case modeling is to separate user concerns. In addition, use cases help us to model the dependencies between concerns through several defined use-case relationships: include, generalization, and extend.

The include relationship allows you to factor out common behaviors between use cases. So, instead of describing such behaviors repeatedly, you only need to include them. The generalization relationship between use cases is similar in meaning to classes. It connotes an “is-a-kind-of” semantics between two use cases and is used when the sequence of actions of one concrete use case (the child) is similar to, but refines those of, the other abstract use case (the parent). For example, Reserve Facility is an abstract use case that describes how to reserve any kind of facility in generic terms, and the Reserve Room use case depicted in Figure 3-1 is a concrete use case that deals with the specifics of Room Reservation.

The extend relationship allows us to add behavior to a base use case at a set of extension points without changing the base use case. The added behavior is specified in the extending use case, which is inserted into the base use case. An extension point unambiguously references a point in a base use-case description. At this point (and possibly using before and after qualifiers), the extension use-case flow as specified in the extending use case is inserted when the use case is interpreted. You probably notice that there is some resemblance between use-case extensions and aspects discussed in Chapter 2. This is important because the resemblance implies a seamless transition as we move from use cases to implementation (with aspects), as you shall see in Chapter 4, “Tomorrow with Use-Case Modules.”

Use-Case-Driven Development

Use cases are not just a requirements technique. They are a software engineering technique used to drive the whole software development life cycle. All software development should be driven toward meeting user concerns. You should capture users’ concerns, design the system to fit those concerns, and test the system against those concerns. As we mentioned earlier, use cases express stakeholder concerns and provide early validation of what needs to be built. Use cases are therefore a useful means to drive the development of a system. You build a system incrementally, use case by use case.

Use-case-driven development assumes that software development is model-driven. In its simplest form, this follows a sequence of models: use-case model to design model to implementation model. With each iteration of the software life cycle, the team performs the following activities:

  1. Find the use cases and specify each one.

  2. Design each use case.

  3. Design and implement each class.

  4. Test each use case.

Usually, each of the four activities represents a job to be taken on by one of the team members. Apart from designing and implementing each component, all the rest of the activities above are use-case-based. During these activities, we develop the following key elements: use cases, use-case realizations, and components.

There is of course more work to be done within an iteration, such as architectural analysis and design, but we deal with that in a later part of the book.

The use-case model describes the system from an external perspective—the internal building blocks are not represented in this perspective. The internals of the system are introduced in the design model. Each use case in the use-case model is realized by one use-case realization in the design model. A use-case realization is a UML collaboration describing (using, for instance, interaction diagrams) which classes participate, how they interact, and what responsibilities they take on to realize a use case. Figure 3-2 shows how three peer use cases in the Hotel Management System get mapped into their respective realizations and subsequently to classes. These use cases are considered peers because they are separate from one another, but their realizations make use of similar classes.

From use cases to classes.

Figure 3-2. From use cases to classes.

Each use case is designed, and the result is a use-case realization. From each use-case realization, you identify what responsibilities are imposed on each class. You then collate the responsibilities for each class and design, implement, and test that class.

Based on the use-case realizations, many of today’s tools can generate a specification for each class by collecting all the responsibilities assigned to the class over all use cases. This is quite straightforward, since the responsibilities come directly from the use-case realizations.

The job for the class owner is to collect the responsibilities of each class from all relevant use-case realizations. The class owner also needs to reconcile the different needs of the use cases. For example, the realization of two use cases might result in the same operation in the same class but with slightly different behaviors. In addition, the class owner has to consider details such as concurrency (e.g., deadlock). The class owner tests the implementation of each class against the collated responsibilities, and finally, the entire use-case realization is tested.

Roles and Benefits of Use Cases

The role of use cases is important, and the benefits are many. Use cases are important tools to capture stakeholder concerns effectively. In fact, the majority of all concerns are extremely well mapped to use cases. When describing use cases, you walk through the desired behavior of the system step by step: what the actor does and what the system does in response. This helps you describe the context in which stakeholders’ concerns and requirements apply. That is why we dare say that well-expressed concerns are well-expressed use cases.

In addition, use cases are advantageous in driving the development of a system.

  • Since use cases are the behavior of the system from the user’s perspective, they serve very well as preliminary test cases and provide early validation of acceptance criteria.

  • Use cases thread through classes and drive how classes work together.

  • Use cases help in planning: high-priority and critical use cases can be determined early and implemented first.

Use cases are useful for building systems in general and are not limited just to object-oriented systems. The use-case technique has been extended to incorporate business modeling, user experience modeling, systems engineering (including hardware systems), and more.

Many books have been written about use cases, and with the wealth of literature and experience behind us, it is no wonder that it is fast becoming a de facto standard in capturing requirements and driving software development.

Gaps in the Use-Case Technique

You have seen that use cases are good for capturing concerns and driving software development. But there is a problem, which is evident in Figure 3-2. Even though peer use cases are separate during requirements, their separation is not preserved during implementation. The realization of a use case touches many classes (scattering), and a class contains pieces of several use-case realizations (tangling). As a serious consequence, the realization of each use case gets dissolved in a sea of classes.

Recall that in use-case modeling, there are include, generalization, and extend relationships to relate different use cases. For the include relationship, we have the technique for a use-case realization to make use of another use-case realization. For the generalization relationship, we have the technique for a use-case realization to inherit from an abstract use-case realization. However, we have a problem when it comes to the extend relationship. We had previously no mainstream programming language supporting the implementation of extensions as we now get with AOP. The effect is that it is not possible to separate extension use cases from base use cases in design and implementation. The only way to implement extension use cases is to write some code in the base to invoke the extension. This runs counter to the idea of keeping extensions separate.

The major problem today is that traditional languages don’t support separation of crosscutting concerns, so the impacts of the different use cases on the class can’t be kept separate. As a consequence, you cannot keep peer and extension use cases separate during implementation.

Bridging the Gaps with Aspects

The two gaps we highlighted in the previous section by no means invalidate the usefulness of use-case-driven development. In fact, any development approach that does not have a means to maintain the modularity of concerns all the way down suffers from the same consequence, and all current approaches do.

From our discussion in the previous chapter, you know that aspects can help keep concerns separate during implementation, and they can certainly be used to bridge the gaps in use-case-driven development, as you see in Chapter 4.

But use cases are not the only one to benefit from aspects. Aspects benefit from use cases as well. The popularity of the use-case technique implies that a wealth of knowledge already exists on use cases regarding the modeling of concerns and regarding driving software development. It will be a tremendous advantage if this knowledge can be harvested and applied to aspect orientation. This is important because aspect orientation has been missing methodological guidance until now.

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

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