Writing Text-Based Behavioral Specifications


One of the most common ways to capture the details of a behavior is to use text-based specifications—using text in a semiformal way to express what the behavior does. You have several different forms to choose from, based on what you exactly want to accomplish, your tools, and your organization’s standards. For example, you may have to type these text-based specifications directly into your UML tool after opening up the use case ovals or you may need to place them in separate documents. You may have strict templates to follow or you may have considerable freedom. And, you may use a different approach when documenting use case behaviors than when documenting operation behaviors. With whatever restriction you have, keep the following considerations in mind when using text-based approaches:

  • Don’t descend to pure functional thinking. Pay attention to the objects that are performing the behavior or the behavior is being performed on.

  • Consider your audience. Text-based approaches can easily become exercises of codelike complexity or pure expressions of logic and set theory. Unless your audience will understand what you are writing, you’re wasting your time.

  • Choose the right level of abstraction. Keep away from lower-level details unless you need them to explain the intent of the behavior. Object-oriented approaches tend to have many small behaviors that collaborate to accomplish larger goals. Sometimes you’ll be documenting those small behaviors; sometimes you’ll be documenting how they collaborate. The description of the behaviors should be consistent with the current level of abstraction—which you have a chance to bring lower when you write the code itself.

  • Maximize cohesion. Follow this traditional advice for any design of behavior. When you trigger a behavior, the effects should be all working together to a common goal. If a part or piece of behavior seems extraneous, drop it or move it somewhere else. If the parts and behaviors work together well, and all of them are needed, then you have high cohesion. If the name of the behavior, operation, or use case requires an and, reconsider if the behavior is properly focused on a single coherent behavior. For example, if the use case were called Reserve Room and Order Room Service, you’d know pretty quickly that the use case is trying to do too much.

Writing use-case specifications

The traditional documentation approaches for use cases (discussed in Chapter 9) are possible choices for behavioral specification. Although there are several different ways of documenting use cases, they typically describe one main course (flow), with alternatives described afterwards, without disturbing the main flow.

I recommend using this main-and-alternate-flow approach whenever the requirements for a behavior seem unclear, and when the complexity of seeing too many scenarios at once starts to boggle your mind. It’s very good for documenting externally visible behavior and requirements—but this technique isn’t quite as good for capturing algorithmic, design, or implementation ideas.

Writing pre- and postconditions

One common and very useful style of documenting behaviors of all sorts that you may use is the establishing of pre- and postconditions. These may be used along with other text-based approaches, or with the graphical approaches:

  • Precondition: A precondition is a statement that must be true about the world before a behavior is started. Its existence serves to guarantee that the behavior proceeds as planned. For example, before you can cancel a room reservation, there must be an active room reservation, and you must be the reserver or a representative.

  • Postcondition: A postcondition is any statement that must be true about the world after the behavior successfully completes. For example, after you cancel a room reservation, the room is marked as free and any credit hold on your card is dropped.

  • Invariants: Besides pre- and postconditions, you must guarantee your invariants—conditions that must be true both before and after a behavior executes. For example, the number of occupants for a room on a given day is never, never less than zero.

 Technical Stuff   Invariants are really conditions that must be true any time another object queries (or looks at) the object executing the operation. In the presence of multithreading, where an object can do more than one thing at a time, it’s possible for an object to be executing an operation while reporting on its condition. This means that the invariant can’t be violated even temporarily while the operation is running.

When you supply a complete set of preconditions and postconditions for a behavior, you define that behavior without implying a design. Any caller or invoker of a behavior or operation tries to guarantee that the preconditions are met before the behavior is called. Then the object offering the operation guarantees that the postconditions are met—after the behavior finishes. This approach is sometimes called design-by-contract. It allows the designers to do whatever they want as long as the contract is upheld.

Writing OCL constraints

Though you can write constraints in any language, you may use a special language that is part of UML when writing these constraints. The Object Constraint Language (OCL) was built upon the underlying concepts of UML and can refer explicitly to the objects, attributes, and links within your own class diagrams. By using OCL, you can be sure that your constraints are unambiguous.

OCL is a very complex and complete language. If you use OCL for complex expressions, you tend to sacrifice readability for precision. However, with some of the UML tools, the OCL may be formally processed and verified. If you can properly construct the OCL constraint, it means that you have enough information in your models to enforce the constraint. When you write in a natural language such as English, you can easily write a constraint that just cannot be enforced because there is missing information in the model. Of course, knowing it’s possible to enforce a constraint doesn’t mean the enforcement is easy.

When you use OCL constraints, you refer directly to features that appear on the class diagram—for example, classes, attributes, roles, and operations. This direct reference prevents you from divorcing your functional definition from the objects the behavior actually operates on.

Harnessing OCL constraint syntax and applying the OCL dot operator

When you’re writing an OCL constraint, you usually attach it to an operation in a note box (see Figure 11-4). Here’s the basic syntax for OCL constraints for operations:

 context Type::behaviorName(para1:Type1, . . .): ReturnType pre ConstraintName: OCLExpression post ConstraintName: OCLExpression inv ConstraintName: OCLExpression 

The following list details the syntax used in OCL constraints for operations:

  • context: The keyword that starts up the OCL constraints. It precedes the definition of the constraint context, where the applicability of the constraints is indicated.

  • Type: The subject of this behavior. It’s the name, the system, subsystem, class, or type where you’re defining the behavior.

  • behaviorName: The name of the operation or use case.

  • para1:Type1, . . . : The parameter list for the behavior.

  • ReturnType: The type of any return value from the behavior.

  • pre, post, or inv: Keywords that indicate the type of constraint. They indicate precondition, postcondition, and invariant respectively.

  • ConstraintName: An optional name for the constraint so that it can be referred to again.

  • OCLExpression: A logical expression that must evaluate to true or false.

We show an example in Figure 11-4, using both pre- and postconditions on the operation Reservation::cancel().


Figure 11-4: Pre- and post- conditions usiing OCL.

In this example, the Reservation::cancel() operation has three parameters: a number (num) and two dates (start and end). There are also two preconditions and two postconditions.

First, the context keyword establishes that this set of constraints is for the operation Reservation::cancel(), that is, the operation cancel defined in the class Reservation. The context also defines the object that owns the operation. You can refer to the owning object (the object of the class Reservation that is running the operation) by using the keyword self.

The first constraint in Figure 11-4, the precondition named pr1, refers to the isCanceled attribute of the self object and requires it to have the value of the enumerated literal False, as defined in the Boolean type. (An enumerated literal is one of the possible values of finite-valued type where all the possible values are listed when the type is defined.) What it’s saying is that you can’t cancel an already-canceled reservation.

The dot (.) operator in OCL has several possible meanings, as shown in Table 11-2.

Table 11-2: OCL Dot Operator

A.B

Refers to

object.attribute

The attribute(s) B of the object A

object.queryOperation

The results of calling the query operation B on the object A. Query operations return values but doesn’t change any values.

Class.staticAttribute

The static attribute B of the class A. Static attributes are owned by a class as a whole and not by individual objects.

object.rolename

The set of object(s) playing the role B across the association from A

EnumeratedType.literal

The value represented by the literal B of the enumerated datatype A

The dot operator has one more property that is interesting; you can successively apply it to the results of a previous dot operation. In practical terms, A.B.C is the same as (A.B).C.

 Tip   You might take advantage of some common ways of reading complex OCL dot expressions, such as A.B.C can be read as “A’s B’s C” or “the C of the B of the A”.

The second constraint in Figure 10-4, the precondition named pr2, requires the input parameter num to match either the confirmation number or the credit number of the reserver. Using the approach discussed above to read these complex statements. The expression self.reserver.creditCard refers to self’s (current object) reserver’s creditCard, or the creditCard of the reserver of the current object (self). If the creditCard number is used the correct startDate and stopDate must also be supplied.

Finally, two postconditions are shown in Figure 11-4. The first requires the operation to leave the isCanceled flag set to true. The second indicates that the result of the operation is also set to true.

Writing general algorithms

You may have occasion to specify a mathematical, scientific, or computer- science algorithm. This is rare for most developers, but if you find yourself in these situations, it’s usually best simply to refer to a document where the algorithm is predefined.

You may define an algorithm with pseudocode—codelike sequences of characters that describe an operation. The purpose of pseudocode is to describe an algorithm in sufficient detail so (technically oriented) non-programmers can understand it, without forcing the use of a particular programming language.

 Warning   When using pseudocode, describe the essence of the algorithm—don’t go too deep by writing nearly pure code. You may find this goal hard to achieve. We generally recommend utilizing alternative graphical techniques other than writing pseudocode. You can see some of these techniques in Chapters 12, 13, and 14.

 Tip   UML developers forced to use pseudocode (whether by corporate standards or because the operation uses lots of algorithms) often base their pseudocode on OCL. Unfortunately, OCL is only a constraint language; it can’t actually change the value of anything. In addition, OCL has only limited control structures. The common strategy uses two different syntaxes:

  • You can adopt the syntax of your current programming language for assignment and control statements.

  • When you need to refer to elements in the class diagram, use the OCL dot notation as your navigation syntax.

Using this approach for writing OCL-based pseudocode can help you design and write creditable algorithms.

Another approach may soon be commonly possible. Recently added to UML, and formally incorporated into UML 2 are the Action Semantics. The Action Semantics define a metamodel (a model made up of models) for specifying behavior independent of implementation—that is, suitable for automatic machine translation into various implementations for various architectures. This is part of OMG’s Model-Driven Architecture (MDA) allowing developers to skip writing code in programming languages. By constructing very complete models and formally defining the behaviors, developers can target implementation on different platforms or architectures without changing the models.

Several syntaxes for the Action Semantics are possible. Different tools support MDA differently—for different types of problems and different ranges of architectures. Tool support is already available in the embedded and real-time development areas.




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