2.3. OperationsOperations are features of classes that specify how to invoke a particular behavior. For example, a class may offer an operation to draw a rectangle on the screen or count the number of items selected in a list. UML makes a clear distinction between the specification of how to invoke a behavior (an operation) and the actual implementation of that behavior (a method). See "Methods" for more information. You place operations in a separate compartment with the following syntax: visibility name ( parameters ) : return-type {properties} where parameters are written as: direction parameter_name : type [ multiplicity ] = default_value { properties } Figure 2-13 shows several example operations on a class. Figure 2-13. Example operations on a classThe syntax elements are:
Parameter syntax elements are:
2.3.1. Operation ConstraintsAn operation may have several constraints associated with it that help define how the operation interacts with the rest of the system. Together, constraints on an operation establish a contract that an implementation of the operation must obey. Constraints on an operation follow the usual constraint notation and are placed either immediately after the operation signature or in a note attached with a dashed line. See "Constraints" for more information. 2.3.1.1. PreconditionsPreconditions capture what the state of the system must be before an operation can be invoked. Practically speaking, you really can't express the state of the entire system. Instead, preconditions typically express the valid values for parameters, the state of the class owning the operation, or a few key attributes of the system. The specification explicitly states that the operation doesn't need to check the preconditions in the body of the operation before executing; theoretically the operation will not even be invoked if the preconditions aren't met. In practice, few languages offer such protection. If someone took the time to express them, it is usually in your best interest to verify the preconditions are correct when implementing an operation. As a developer, preconditions offer you one of the few chances to "cover your butt" and say exactly how you expect things to be when your implementation is invoked; use them. Figure 2-14 shows several examples of preconditions. Figure 2-14. Preconditions for operations2.3.1.2. PostconditionsPostconditions capture guarantees about the state of the system after an operation has executed. Like preconditions, postconditions typically express the state of one or more key attributes of the system, or some guarantee about the state of the class owning the operation. Figure 2-15 shows example postconditions linked to an operation. Figure 2-15. Postconditions for operations2.3.1.3. Body conditionsAn operation may have a bodyCondition that constrains the return value. The bodyCondition is separate from the postcondition because the bodyCondition may be replaced by methods of subclasses of the owning class. (See "Generalization" for more information on subclasses.) For example, a class named Window may specify a body condition for a method named getSize( ) that requires the length and width of a window be nonzero. A subclass named SquareWindow may provide its own body condition stating that the width must equal the height. The bodyCondition is similar to the pre- and postconditions in that the constraint may be expressed in natural language or in OCL. See "Constraints" for more information. Figure 2-16 shows an example of a bodyCondition on an operation. Figure 2-16. Body conditions for operations2.3.1.4. Query operationsAn operation may be declared as a query operation if the implementation of the operation doesn't modify the owning class in any way. In practice, modelers often use the query property to indicate a method that doesn't change any meaningful attribute of an object. For example, there may be internal cache attributes that are updated as a result of a query. The important thing is that the state of the system, from an external perspective, isn't changed by the query method; there can be no side effects to calling the method. You indicate a query method by placing the query constraint after the operation signature. For example, an operation named getAge( ) that simply returns an integer without changing any internal value of the owning class would be considered a query method. In C++, this typically maps to a const method. Figure 2-17 shows several query methods on a class. Figure 2-17. Example query operations2.3.1.5. ExceptionsWhile they're technically not constraints, you may express exceptions thrown by an operation using similar notation. Exceptions are typically other classes (often stereotyped with the keyword «exception», though this is simply by convention) that are thrown by an operation in the event of an error. You can list thrown exceptions in a note attached to an operation using a dashed line. Figure 2-18 shows an example of an operation that throws several exceptions. Figure 2-18. A method that throws several exceptions2.3.2. Static OperationsOperations typically specify behavior for an instance of a class. However, UML allows for an operation to specify behavior for the class itself. These operations are called static operations and are invoked directly on the class, not on an instance. Static operations are frequently used as utility operations that don't need to use the attributes of the owning class. UML doesn't formally discuss the notation of these operations, but it is typical to see them represented with the same convention as static attributes. You indicate an operation is static by underlining the operation signature. Figure 2-19 shows an example of a static operation. Figure 2-19. A class with a static operation |