Interaction Diagrams in Software Engineering: A Comprehensive Guide, Lecture notes of Object Oriented Analysis and Design

Object Oriented System Analysis and Design Lecture Notes for Addis Ababa Science and Technology University

Typology: Lecture notes

2018/2019

Uploaded on 10/27/2019

yonasg_
yonasg_ 🇪🇹

6 documents

1 / 18

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
Chapter 05: Interaction diagrams
Interaction diagrams are used to model the dynamic aspects of a software system they help to
visualize how the system runs. They show how a set of actors and objects communicate with
each other to perform the steps of a use case, or of some other piece of functionality. The set of
steps, taken together, is called an interaction.
Interaction diagrams can show several different types of communication. These include
messages exchanged over a network, simple procedure calls, and commands issued by an actor
through the user interface. Collectively, these are referred to as messages.
The following elements can be found in an interaction diagram:
Instances of classes. Instances of classes (i.e. objects) are shown as boxes with the class and
object identifier underlined.
Actors. are shown using the same stick-person symbol as in use case diagrams.
Messages. These are shown as arrows from actor to object, or from object to object. One of the
main objectives of drawing interaction diagrams is to better understand the sequence of
messages.
Create an interaction diagram.
Two kinds of diagrams are used to show interactions: sequence diagrams and communication
diagrams.
Both contain similar information about an interaction, although sequence diagrams have
notations that make them somewhat more powerful. Sequence diagrams explicitly show the
sequence of events on a time line, whereas communication diagrams are more compact.
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12

Partial preview of the text

Download Interaction Diagrams in Software Engineering: A Comprehensive Guide and more Lecture notes Object Oriented Analysis and Design in PDF only on Docsity!

Chapter 05: Interaction diagrams

Interaction diagrams are used to model the dynamic aspects of a software system – they help to visualize how the system runs. They show how a set of actors and objects communicate with each other to perform the steps of a use case, or of some other piece of functionality. The set of steps, taken together, is called an interaction.

Interaction diagrams can show several different types of communication. These include messages exchanged over a network, simple procedure calls, and commands issued by an actor through the user interface. Collectively, these are referred to as messages.

The following elements can be found in an interaction diagram:

Instances of classes. Instances of classes (i.e. objects) are shown as boxes with the class and object identifier underlined.

Actors. are shown using the same stick-person symbol as in use case diagrams.

Messages. These are shown as arrows from actor to object, or from object to object. One of the main objectives of drawing interaction diagrams is to better understand the sequence of messages.

Create an interaction diagram.

Two kinds of diagrams are used to show interactions: sequence diagrams and communication diagrams.

Both contain similar information about an interaction, although sequence diagrams have notations that make them somewhat more powerful. Sequence diagrams explicitly show the sequence of events on a time line, whereas communication diagrams are more compact.

Sequence diagrams

A sequence diagram shows the sequence of messages exchanged by the set of objects (and optionally an actor) performing a certain task. Figure. gives an example.

The objects are arranged from left to right across the diagram – an actor that initiates the interaction is often shown on the left. The vertical dimension represents time. The top of the diagram is the starting point, and time progresses downwards towards the bottom of the diagram. A vertical dashed line, called a lifeline , is attached to each object or actor. The lifeline becomes a box, called an activation box , during the period of time that the object is performing computations. The object is said to have live activation during these times.

A message is represented as an arrow between activation boxes of the sender and receiver. You give each message a label; it can optionally have an argument list and a response.

Figure 5.

the same interactions as the sequence diagrams of Figures 5.1 and 5.3. Communication links can exist between two objects whenever it is possible for one object to send a message to the other one. Several situations can make this possible:

■ The classes of the two objects are joined by an association. This is the most common case. In Figure 5.6, message 2 is sent over such a link. If all messages are sent in the same direction, then probably the association can be made unidirectional. ■ The receiving object is stored in a local variable of the sending method (but the objects are not yet joined by an association). This can happen when the receiving object is created in the sending method, such as in message 4 of Figure 5.6, or when some computation returns an object that is only kept in a local variable. In message 1 of Figure 5.6 we tag such a message with the stereotype local. ■ A reference to the receiving object has been received as a parameter of an earlier message to the sender. In Figure 5.6, we tag such messages with the stereotype parameter. For example, in message 3 the Student was previously passed as a parameter to the CourseSection. ■ The receiving object is global. This is the case when a reference to an object can be obtained using a public static method (e.g. using the Singleton pattern). The stereotype global could be used in this case. Note that the use of global data should be minimized. ■ The objects communicate over a network. The stereotype network could be used to show this.

State diagrams A state diagram , also known as a state machine diagram , is another way of expressing dynamic information about a system. It is used to describe the externally visible behavior of a system or of an individual object.

At any given point in time, the system or object is said to be in a certain state. It remains in this state until an event occurs that causes it to change state. Being in a state means that it behaves in a specific way in response to any events that occur. You represent a state using a rounded rectangle that contains the name of the state.

There are two other special symbols that can appear on a state diagram:

■ A black circle represents the start state. When the system or object starts running, it immediately takes a transition from the start state to a regular state. There should be only one start state in each top-level state diagram, and there should be only one unlabelled transition pointing out of the start state. ■ A black circle with a ring around it represents an end state. The system or object finishes its work when such a state is reached. There can be more than one end state in a state diagram. The symbol is supposed to resemble a target. In addition, there are several other pieces of notation that can be placed inside states and on transitions to describe their behavior more precisely. We will discuss these in the context of examples below.

Figure 5.7 represents the game of tic-tac-toe, also known as noughts and crosses. Since player X always goes first, the initial transition from the start state points to the ‘X Turn’ state. From then on, the game alternates between ‘X Turn’ and ‘O Turn’ states, until the game ends. There can be three possible outcomes of the game, represented by the three end states: X can win, O can win or there can be a tie. The ‘Tie’ end state can be reached from both ‘X Turn’ state and ‘O Turn’ state.

Notice that there is more than one transition leading out of both ‘X turn’ and ‘O turn’ states. In this situation, the system will take the transition that corresponds to the first occurring event: a move resulting in a win, a tie or continued play. In the remainder of this chapter we will use event labels of various kinds to make clear what causes each transition.

Elapsed-time transitions

The event that triggers a transition can be a certain amount of elapsed time. Figure 8.12(a) illustrates the use of such elapsed-time transitions to model a simple traffic signal.

The figure shows the behavior of instances of the CourseSection class shown in Figure 5.2 When it is first created, a CourseSection is in ‘Planned’ state and is not yet ready to receive students. When registration is opened, the system moves to ‘OpenNotEnoughStudents’ state.

In this state, the CourseSection can accept requests to register, but the course will not actually be taught until the class size reaches a certain minimum. The object evaluates the classSize >= minimum condition every time anything occurs that could make the condition true; as soon as it becomes true, a transition is taken to ‘OpenEnoughStudents’ state.

Activities and actions in state diagrams You can represent two kinds of computations using state diagrams. These computations are called activities and actions. An activity is something that occurs over a period of time and takes place while the system is in a state. The system may take a transition out of the state in response to completion of the activity. However, if some other transition is triggered first, then the system has to terminate the activity as it leaves the state. An activity is shown textually within a state box by the word ‘do’ followed by a ‘/’ symbol, and a description of what is to be done. When you have details such as actions in a state, you draw a horizontal line above them to separate them from the state name.

Figure 5.11 State diagram for a jukebox, illustrating an activity in a state

Figure 5.11 shows a jukebox with just two states. In ‘ProposeSelection’ state, the system waits for the user to press a button, selecting some music. In the ‘MusicPlaying’ state, the system plays

the chosen music until it comes to an end. The system then takes a transition back to ‘ProposeSelection’ state. An action is something that takes place effectively instantaneously in any of the following situations: ■ When the system takes a particular transition. ■ Upon entry into a particular state, no matter which transition causes entry into that state. ■ Upon exit from a particular state, no matter which transition is being taken.

An action should take place with no noticeable consumption of time; therefore it should be something simple, such as sending a message, starting a hardware device or setting a variable. An action is always shown preceded by a slash (‘/’) symbol. If the action is to be performed during a transition, then the syntax is event/action. If the action is to be performed when entering or exiting a state, then it is written in the state box with the notation enter/action or exit/action.

Figure 5.12 State diagram for a garage door opener, showing actions triggered by entry into a state

Figure 5.12 illustrates the use of actions in the state diagram for a garage door opener. Upon entry into each state, a particular action occurs that has an effect on the garage door motor: the motor controller can be told to start the motor running forwards (opening the door), to start the motor running in reverse (closing the door), or to stop the motor

Nested substates and guard conditions A state diagram can be nested inside a state. The states of the inner diagram are called substates. Figure 5.13 shows a state diagram of an automatic transmission; at the top level this has three states: ‘Neutral’, ‘Reverse’ and a driving state, which is not explicitly named. The driving state is divided into substates corresponding to the three gears that the system automatically chooses. The advantage of the nesting is that it shows compactly that the driving substates are all very similar to each other – in particular, that they can all transition to ‘Neutral’ at any time, upon the user’s command. The start symbol inside the driving state shows that it by default starts at the ‘First’ substate. However, the user can also manually select ‘First’ or ‘Second’ to force the transmission to move into, and stay in, these substates.

■ A fork has one incoming transition and multiple outgoing transitions. The result is that execution splits into two concurrent threads. ■ A join has multiple incoming transitions and one outgoing transition. The outgoing transition will be taken only when all incoming transitions have been triggered. The incoming transitions must be triggered in separate threads. ■ A rendezvous has multiple incoming and multiple outgoing transitions. Once all the incoming transitions are triggered, the system takes all the outgoing transitions, each in a separate thread. An activity diagram also has two types of nodes for branching within a single thread. These are represented as small diamonds: ■ A decision node has one incoming transition and multiple outgoing transitions, each with a Boolean guard in square brackets. Exactly one of the outgoing transitions will be taken. ■ A merge node has two incoming transitions and one outgoing transition. It is used to bring together paths that had been split by decision nodes.

Figure 5.15 Activity diagram of the registration process Figure 5.15 shows an example activity diagram for the process of registering in a course section. The first thing that occurs is the reception of the registration request. Once this processing is complete, the system immediately takes the outgoing transition and forks the processing into two concurrent threads. The concurrent thread on the right is responsible for checking whether a course is full or not. The outcome of its computations is either that the course is not full, in which case execution proceeds to the join, or else the course is full, in which case the whole activity diagram terminates. The concurrent thread on the left, meanwhile, checks whether the student is allowed to register. First it checks if the student has the prerequisites for the course; if this check is affirmative, the thread proceeds to the join via a merge node. Otherwise a second check is performed, to see if the student has special permission that can override the lack of prerequisites. If this second check is affirmative, the thread proceeds to the join via the merge node, otherwise registration is disallowed and the whole activity diagram terminates.

Either concurrent thread may reach the join first, at which time it will wait for the other. It may be the case that neither thread, or only one thread, ever reaches the join, due to the course being full or registration being disallowed. However, if both threads reach the join, then they are replaced by a single thread that performs the final activity – completing the registration.

Difficulties and risks in modeling interactions and behavior

Dynamic modeling is a difficult skill, particularly because in a large system there are a very large number of possible paths a system can take, and because it is hard to choose which classes to allocate to each behavior. Resolution. The general resolution of this difficulty is the same as for static modeling: ensure that skilled developers lead the process and ensure that all aspects of your models are properly reviewed. Also, work iteratively: develop initial class diagrams, use cases, responsibilities, interaction diagrams and state diagrams; then go back and verify that all of these are consistent, modifying them as necessary. Drawing different diagrams that capture related, but distinct, information will often highlight problems. In safety-critical systems it is important to use tools that can help you animate, analyze and verify your models.

Principles leading to good design

In this section we introduce you to general principles you should apply whenever you are designing software. Applying these principles diligently will result in designs that have many advantages over designs in which the principles were not applied.

Some overall goals we want to achieve when doing good design are:

■ Increasing profit by reducing cost and increasing revenue. For most organizations, this is the central objective. However, there are a number of ways to reduce cost, and also many different ways to increase the revenue generated by software. ■ Ensuring that we actually conform to the requirements, thus solving the customers’ problems. ■ Accelerating development. This helps reduce short-term costs, helps ensure the software reaches the market soon enough to compete effectively, and may be essential to meet some deadline faced by the customer. ■ Increasing qualities such as usability, efficiency, reliability, maintainability and reusability. These can help reduce costs and also increase revenues.

Design Principle 1: Divide and conquer The divide and conquer principle dates back to the earliest days of organized human activity. Trying to deal with something big all at once is normally much harder than dealing with a series of smaller things. Military campaigns are waged this way: commanders try to avoid fighting on all fronts at once. Cars are also built using the divide and conquer strategy: some people design the engines while others design the body, etc

In software engineering, the divide and conquer principle is applied in many ways. We have already seen how the process of development is divided into activities such as requirements

Design Principle 3: Reduce coupling where possible

Coupling occurs when there are interdependencies between one module and another. Figure illustrates the concept of a tightly coupled and loosely coupled system.

In general, the more tightly coupled a set of modules is, the harder it is to understand and, hence, change the system. Two reasons for this are: ■ When interdependencies exist, changes in one place will require changes somewhere else. Requiring changes to be made in more than one place is problematic since it is time-consuming to find the different places that need changing, and it is likely that errors will be made. ■ A network of interdependencies makes it hard to see at a glance how some component works. Additionally, coupling implies that if you want to reuse one module, you will also have to import those with which it is coupled. This is because the coupled components need each other in order to work properly.

Cloning should normally not be seen as an effective form of reuse. Cloning involves copying code from one place to another; it should be avoided since, when there are two or more occurrences of the same or similar code in the system, any changes made (e.g. to fix defects) will have to be made in all clones. Unfortunately, maintainers are often not aware of all the clones that exist, and hence only make the change in one place. The bug thus remains, even though the maintainer thinks it is fixed. In general, it can be acceptable to clone a single line of code; perhaps a line that contains a complicated call to a method with many arguments. However, any time you are tempted to clone more than a couple of lines of code, it is normally best to encapsulate the code in a separate method and call it from all the places it is needed. See the sidebar ‘Tolerating Clones?’ for a discussion of exceptions to this rule.

Design Principle 7: Design for flexibility

Designing for flexibility (also known as adaptability ) means actively anticipating changes that a design may have to undergo in the future and preparing for them. Such changes might include changes in implementation or changes in functional requirements.

Ways to build flexibility into a design include:

■ Reducing coupling and increasing cohesion. This allows you to more readily replace part of a system. ■ Creating abstractions. In particular, try to create interfaces or superclasses with polymorphic operations. Doing this allows new extensions to be easily added. ■ Not hard-coding anything. Constants should be banished from code. ■ Leaving all options open. ■ Using reusable code and making code reusable. The techniques discussed in the previous two design principles, such as adding hooks, tend also to make designs more flexible.

Design Principle 8: Anticipate obsolescence

Anticipation of obsolescence is a special case of design for flexibility. Changes will inevitably occur in the technology a software system uses and in the environment in which it runs. Anticipating obsolescence means planning for evolution of the technology or environment so that the software will continue to run or can be easily changed.

The following are some rules that designers can use to better anticipate obsolescence:

■ Avoid using early releases of technology. The immediate problem is that early releases are likely to have more defects than later releases. ■ Avoid using software libraries that are specific to particular environments. ■ Avoid using undocumented features or little-used features of software libraries. ■ Avoid using reusable software or special hardware from smaller companies, or from those that are less likely to provide long-term support ■ Use standard languages and technologies that are supported by multiple vendors.

Design Principle 9: Design for portability

Designing for portability shares many things in common with anticipating obsolescence, although the objective is different. Anticipating obsolescence has, as its primary objective, the survival of the software. Design for portability has, as its prime objective, the ability to have the software run on as many platforms as possible, although sometimes this might also be a necessity for survival.

An important guideline for achieving portability is to avoid the use of facilities that are specific to one particular environment. Some programming languages, such as Java, make this easy because the language itself is designed to allow software to run on different platforms unchanged. Nevertheless, even with Java, there can be subtle differences regarding how some features work on different platforms – knowing about these and avoiding them is important. One such difference is class libraries; some companies have produced special Java libraries that work only with that company’s compiler, which in turn runs on only one platform. Attempting to port software that uses that library to another platform can be difficult.

Design Principle 10: Design for testability

During design you can take steps to make testing easier. Testing, which is the subject of the next chapter, can be performed both manually and automatically. Automatic testing involves writing a program that will provide various inputs to the system in order to test it thoroughly. Therefore it pays to design a system so that automatic testing is made easy.

The most important way to design for testability is to ensure that all the functionality of the code can be executed without going through the graphical user interface. You can achieve this by carefully separating the UI from the functional layer of the system. A test harness can then be written that calls the API of the functional layer.

Design Principle 11: Design defensively

You should never trust how others will try to use a component you are designing. Just like automobile drivers are taught not to trust other drivers, and therefore to drive defensively, a software designer should not trust other designers or programmers, and so should design defensively. In other words, in order to increase the reliability of your system, you not only need to make sure you don’t add any defects yourself, but you must also properly handle all cases where other code attempts to use your component inappropriately.

The most important way to design defensively is to check that all of the inputs to your component are valid. Or, more accurately, check the preconditions of each component.

Design by contract is a technique that allows you to design defensively in an efficient and systematic way. The key idea behind design by contract is that each method has an explicit contract with its callers. The contract has a set of assertions that state: