2.2 Small Things: Objects, Classes, and Interfaces


The UML has a rather rich set of structural elements and provides diagrammatic views for related sets of them. The UML is intentionally, recursively self-similar in its structural and dynamic concepts. That is, the same set of concepts may be used at different levels of abstraction, from the largest system and subsystems down to small, simple objects. Nevertheless, some concepts are used more at the smaller scale and others more at the larger scale.

A number of elementary structural concepts in the UML show up in user models: object, class, data type, and interface. These structural elements form the basis of the structural design of the user model. In its simplest form, an object is a data structure bound together with operations that act on that data. An object only exists at runtime; that is, while the system is executing, an object may occupy some location in memory at some specific time. The data known to an object are stored in attributes simple, primitive variables local to that object. The behaviors that act on that data are called methods; these are the services invoked by clients of that object (typically other objects) or by other methods existing within the object.

2.2.1 Objects

Structured methods look at a system as a collection of functions decomposed into more primitive functions. Data is secondary in the structured view and concurrency isn't dealt with at all. The object perspective is different in that the fundamental decompositional unit is the object. So what is an object?

The short form:

An object is a cohesive entity that has attributes, behavior, and (optionally) state.

The long form:

Objects represent things that have both data and behavior. Objects may represent real-world things, like dogs, airfoil control surfaces, sensors, or engines. They may represent purely conceptual entities, like bank accounts, trademarks, marriages, or lists. They can be visual things, like fonts, letters, ideographs, histograms, polygons, lines, or circles. All these things have various features, such as

  • Attributes (data)

  • Behavior (operations or methods)

  • State (memory)

  • Identity

  • Responsibilities

For example, a real-world thing might be a sensor that can detect and report both a linear value and its rate of change.

Sensor Object

Attributes

Behavior

State

Identity

Responsibility

  • Linear value

  • Rate of change (RoC)

  • Acquire

  • Report

  • Reset

  • Zero

  • Enable

  • Disable

  • Last value

  • Last RoC

Instance for robot arm joint

  • Provide information for the precise location of the end of the robot arm with respect to some reference coordinate frame

The sensor object contains two attributes: the monitored sensor value and its computed rate of change (RoC). The behaviors support data acquisition and reporting. They also permit configuration of the sensor. The object state consists of the last acquired/computed values. The identity specifies exactly which object instance is under discussion. The responsibility of the sensor is defined to be how it contributes to the overall system functionality. Its attributes and behaviors must collaborate to help the object achieve its responsibilities

An airline flight is a conceptual thing, but is nonetheless an important object as well.

Airline Flight Object

Attributes

Behavior

State

Identity

Responsibility

  • Flight number

  • Departure time

  • Arrival time

  • Flight plan

  • Depart

  • Arrive

  • Adjust course

Current location (x, y, z, t)

  • Today's flight NW394 to Minneapolis

  • Transfer luggage and passengers to destination

  • File flight plan

  • Adhere to flight plan

Certain of these characteristics (i.e., attributes, behaviors, or state) may be more important for some objects than for others. One could envision a sensor class that had no state whenever you asked it for information, it sampled the data and returned it, rather than storing it internally. An array of numbers is an object that may have interesting information but doesn't have any really interesting behaviors.

The key idea of objects is that they combine these properties into a single cohesive entity. The structured approach to software design deals with data and functions as totally separate entities. Data flow diagrams show both data flow and data processes. Data can be decomposed if necessary. Independently, structure charts show the static call tree to decompose functions (somewhat loosely related to the data processes). Objects, on the other hand, fuse related data and functions together. The object is the fundamental unit of decomposition in object-oriented programming.

Abstraction is the process of identifying the key aspects of something and ignoring the rest. A chair is an abstraction defined as "a piece of furniture with at least one leg, a back, and a flat surface for sitting." That some chairs are made of wood while others may be plastic or metal is inessential to the abstraction of "chair." When we abstract objects we select only those aspects that are important relative to our point of view. For example, as a runner, my abstraction of dogs is that they are "high-speed teeth delivery systems." The fact that they may have a pancreas or a tail is immaterial to my modeling domain. In Figure 2-1, we see a number of concrete instances of sensors being abstracted in a common concept.

Figure 2-1. Object Abstraction

graphics/02fig01.gif

2.2.2 Classes

Objects exist at runtime; at some point in time, objects (at least software objects) occupy a memory address located in a computer. A class is the specification for a set of objects. Classes exist at design time. A class is the "type" of some set of objects, in the same way that "int" is the "type" of a variable x that may assume different values at runtime, such as 3 and 17, or "double" is the "type" of a variable y that may assume the value 45.5 during runtime. A class specifies both the structure and the defined behavior of the objects instantiated from it. All objects of a class have their own copies of the very same set of typed attributes. Thus, if a thermometer is an instance of class Sensor, then it will have an attribute named linearValue, of type long, and another attribute called rateOfChange, of type double. Each object, in fact, has its own copy of these attributes with which it is free to manipulate and manage, but structurally the objects are all identical.

Similarly, each object has the same set of methods (functions) defined by the class. However, the objects "share" the code for these, so you don't end up replicating identical code when you have many instances of the same class. The methods are provided the information as to which object is being manipulated when the method is called usually invisibly to the programmer.

In their simplest expression, classes are nothing more than abstract data types (ADTs) bound together with operators that manipulate that information. This is a low-level perspective and doesn't capture all of the richness available in the object paradigm. Software developers use such ADTs and operators as low-level mechanisms all the time stacks, queues, trees, and all the other basic data structures are nothing more than objects with specific operations defined. Consider these common ADTs that appear in the table on page 85.

At a low level of abstraction, these are merely ADTs bound together with separate functions to provide the services. Because the concept of a stack is meaningless without both the operations and the data, it makes the most sense to bind these things tightly together they are different aspects of a single concept. This is called strong cohesion the appropriate binding together of inherently tightly coupled properties.

Classes have two important features. Classes may (and usually do) contain attributes. These structural features hold the data or information known to the object. Also, classes have methods behavioral features that provide services that usually manipulate the object's attributes. Classes may also have state machines, a topic that will be discussed in Chapter 3.

2.2.2.1 Attributes

Attributes, in OO-speak, refer to the data encapsulated within an object or specified within a class. It might be the balance of a bank account, the current picture number in an electronic camera, the color of a font, or the owner of a trademark. Some objects may have just one or a small number of simple attributes. Others may be quite rich. In some object-oriented languages, all instances of data types are objects, from the smallest integer type to the most complex aggregate. In C++, in deference to minimizing the differences between C and C++, variables of the elementary data types, such as int and float, are not really objects. Programmers may treat them as if they are objects[4] but C++ does not require it.

[4] Well, almost anyway but that's a programming, rather than a modeling, issue.

Data Structure

Attributes

Operations

Stack

TopOfStack: int

Size: int

Element: DATA

Push

Pop

Full

Empty

Queue

Head: int

Tail: int

Size: int

Element: DATA

Insert

Remove

Full

Empty

Linked List

Next: Node pointer

Previous: Node pointer

Element: DATA

Insert

Remove

Next

Previous

Tree

Left: Node pointer

Right: Node pointer

Element: DATA

Insert

Remove

Next

Previous

2.2.2.2 Methods and Operations

As mentioned, a class is a union of the specification of an abstract data type with the operations that act on that data. Strictly speaking, a class contains methods the implementation of operations. An operation is defined in the UML as "a service that an instance of the class may be requested to perform." Operations have a formal parameter list (a typed list of named values passed to and/or from the operation) and a return value. The parameters and return value are optional. A method is said to be the implementation of an operation; reflexively, an operation specifies a method.

All instantiable classes have two special operations defined: a constructor, which knows how to properly make an instance of the class, and a destructor, which knows how to properly remove an instance of that class. Constructors may be parameterless the so-called default constructor and will use default values of any creational parameters, including the allocation of memory and creation of links to other objects. It is not uncommon to have a number of parameterized constructors as well, which bypass one or more defaults. Constructors are invoked whenever an object is created. A common example is if the "++" operator is defined for a class, then X++ (post increment) actually creates a new instance of the class (of which object X is an instance), although this may be optimized away in some cases. Destructors are parameterless and properly destroy the object, including removing allocated memory when necessary. Improperly written destructors are a common cause of memory leaks and care should be taken to ensure that they properly deallocate any memory that the object exclusively owns.

Not all classes are instantiable. Abstract classes are not instantiated because their specification is not complete. That is, they define an operation but not a corresponding method. In order to create an instance of such a class, the abstract class must be subclassed (see Generalization, later in this chapter) and the subclass must provide a method. In C++ terms, abstract classes contain at least one pure virtual operation shown with the unlikely syntax of assigning the operation the value of zero. For example,

void resetDevice(s: initialValue) = 0

Abstract classes and abstract (i.e., "pure virtual") operations are identified by italics.

2.2.3 Notation

Figure 2-2 shows the relationship between classes (the template, or "cookie cutter") and objects (the instance, or "cookie"). The class defines the features, both structural and behavioral, for all instances of objects for which it is the type. Classes can be shown as a simple named rectangle (called "canonical form") or in a more detailed form. In the detailed form, attributes and methods may be listed in separate compartments, as they are in the figure. The objects are instances of the class and, when shown, depict a "snapshot" of the system at some point in time. This allows us to identify particular values of the attributes at the instant the snapshot was taken. Note that the object name is underlined, but all that is really necessary is to observe the presence of the colon (:), which separates the name of the object, called the role name (such as ThePatientThermometer) from the class that specifies it (such as Sensor).

Figure 2-2. Objects and Classes

graphics/02fig02.gif

Figure 2-3 is similar to a diagram you'd find in the design of a system. The figure shows both classes (e.g., TaskPlan) and object (e.g., Elbow: Motor) as well as associations (relations between classes). Both canonical (simple) and detailed forms of classes are shown.

Figure 2-3. Objects and Classes in Use

graphics/02fig03.gif

It isn't necessary to show any of the features of a class or object on a diagram, or if shown, it isn't necessary (or usually wise) to include all of the attributes or operations. Figure 2-3 shows some of the attributes and methods of the classes. Note that the attributes include their types and the methods include their parameters and return types. The marks by the class features indicate the visibility of those aspects.

The UML provides notation to indicate four levels of visibility. When a feature (attribute, method, or operation) is public, it may be proceeded by a "+", as they are in the figure. Public visibility means that the feature may be accessed by anyone. Protected visibility (indicated by a preceding hash mark, #) means that the feature may be accessed by subclasses (see Generalization in the section on Relations, later in this chapter) but not by clients of the class. Clients may be able to gain access to the data, but must go through a public method do to so. Private visibility is even more restricted than protected only methods inside the same class may access the feature. The last level of visibility, package, is rarely used. It is indicated with a tilde ("~") and means the feature is visible to any element in the same package.

The lines connecting the classes and objects in the figure are called associations and allow the objects to collaborate, that is, to send and receive messages. This will be discussed in more detail later.

Usually diagrams are constructed exclusively with classes rather than by mixing in objects. Because classes are universal, class diagrams define sets of possible object configurations. An object may come and go during the execution of the system, so an object diagram is always a snapshot of the system at some specific point in time. Object diagrams, part of what is known as a system's instance model, may be useful, but class diagrams are far more common.

2.2.4 Interfaces

An interface is a specification of a named contract offered by a class. It primarily consists of a collection of operations, but in UML 2.0, interfaces may also include attributes and a special form of a state machine called a protocol state machine. While not required, interfaces allow you to separate out the specification of a set of services from the implementation of those services. As we've seen, a class contains methods, which include the lines of code that implement the service. An operation is a specification of the service that does not include this implementation. To be well formed, the operation should define the signature for invoking the service, including the required parameters and return value (if any), plus the preconditional and postconditional invariants of the operation. Preconditional invariants are things that must be true prior to the invocation of the service, while postconditional invariants are things that the operation guarantees are true upon its completion.

In UML 1.x, interfaces could only have operations (specifications of methods) but not any implementation; neither could they have attributes or state machines. In UML 2.0, interfaces may have attributes but they are virtualized, meaning that any realizing class must provide an attribute of that type. A class is said to realize an interface if it provides a method for every operation specified in the interface, and an attribute for every virtualized attribute in the interface. The class methods must have the same names, parameters, return values, preconditions, and postconditions of the corresponding operations in the interface. In UML 2.0, interfaces may also have protocol state machines for the specification of the allowed set of sequences of operation invocation for realizing classes. Protocol state machines will be discussed in the next chapter.

Interfaces may be shown in two forms. One looks like a class except for the key word interface placed inside guillemots, as in «interface». This form, called a stereotype in UML, is used when you want to show the operations of the interface. The other form, commonly referred to as the "lollipop" notation, is a small named circle on the side of the class. Both forms are shown in Figure 2-4. When the lollipop is used, only the name of the interface is apparent. When the stereotyped form is used, a list of operations of the interface may be shown. In the figure, the Sensor class is said to depend on the interface iFilter, while the Filter class realizes that interface. In UML 2.0, interfaces may be specified from either "end." The UML 1.x interface (the lollipop end in Figure 2-4) specified provided interfaces, that is, features that the realizing class agrees to provide to its client. The other end (the socket in the figure) is a required interface. This allows consistency checking between what is expected and what is provided in the interfaces.

Figure 2-4. Interfaces

graphics/02fig04.gif

Interfaces are used to ensure interface compliance, that is, the client class can consistently and correctly invoke the services of a server class. There is another means to ensure interface compliance that uses the generalization relation from what is called abstract classes (classes that may not be directly instantiated). Abstract classes define operations but not methods, just as an interface does, and so may be used to ensure interface compliance. Either (or both, for that matter) approach can be used to ensure that the clients and servers connect correctly at runtime. Generalization and other class relations are discussed in the Section 2.3.4.

2.2.5 Messaging

The low-level unit of behavior of an object is an action. There are several kinds of actions defined within the UML. The most common one, called a CallAction, invokes a method defined in the receiving object. Actions may appear in methods (they also appear in state machines, as we will see in the next chapter). Activities, discussed later, are types of behaviors that may be methods. In structured software development, explicit calls were shown on structure charts. While this has the advantage of showing the set of invoked services, it has a number of serious disadvantages, including that the order of calls isn't shown in structure charts, nor are conditional branches, nor is there any way to show other, possibly asynchronous communications. This last is the most serious limitation because much of the communication between objects in real-time systems takes place using techniques other than simple function calls.

In the UML, the logical interface between objects is done with the passing of messages. A message is an abstraction of data and or control information passed from one object to another. Different implementations are possible, such as

  • A function call

  • Mail via a real-time operating system (RTOS)

  • An event via a RTOS

  • An interrupt

  • A semaphore-protected shared resource service call

  • An Ada rendezvous

  • A remote procedure call (RPC) in a distributed system

Early analysis identifies the key messages between objects that collaborate. Later, design elaborates an implementation strategy that defines the synchronization and timing requirements for each message. Internally, the object translates the messages into method calls, event receptions, commands, or data to munch on, as appropriate. Messages generally only occur between object pairs that share a link (an instance of an association that is defined between the classes of those objects). Figure 2-5 illustrates how messages flow between objects in a running system.

Figure 2-5. Sending Messages

graphics/02fig05.gif

Logically, an object's interface is the facade that it presents to the world and is defined by the set of protocols within which the object participates. An interface protocol consists of invariants, which include three things:

  • Preconditions

  • Signature

  • Postconditions

The preconditions are the conditions guaranteed to be true before the message is sent or received. For example, if a parameter is an enumerated type Color, the possible values are preconditions. If a parameter is a pointer that is expected to point to a valid object, then the validity of the pointer is a precondition. Ensuring that preconditions are met is normally the responsibility of the object sending the message. Postconditions are the things guaranteed to be true by the time the message is processed and are the responsibility of the receiver of the message. Postconditions include that the return values are well formed and within range, and the service is properly completed. Other postconditions might be that only a certain set of exceptions will be thrown in the presence of a fault. The message signature is the exact mechanism used for message transfer. This can be a function call with the parameters and return type or RTOS message post/pend pair, or bus message protocol. Most invariants are specified using UML constraints, a topic covered in Section 2.6.

The interface should reflect the essential characteristics of the object that require visibility to other objects. Objects should hide inessential details, especially their internal design. The reasons for this are obvious if a client knows how a server object is structured, client class developers will use that information. As soon as that information is used, the server object structure is no longer free to vary without breaking its clients. This is nothing more or less than the concept of "data hiding," which has been around since the dawn of time.[5] By publishing only enough information to properly use the provided services, objects enforce strong encapsulation. In C++, for example, common practice is to hide data members (making the private or protected visibility), but to publish the operations that manipulate the data (making them public).

[5] Which occurred sometime in the late sixties, I believe ;-).



Real Time UML. Advances in The UML for Real-Time Systems
Real Time UML: Advances in the UML for Real-Time Systems (3rd Edition)
ISBN: 0321160762
EAN: 2147483647
Year: 2003
Pages: 127

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net