tm354 Block3

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 224

TM354 Software engineering

Block 3 Units 9–12


From architecture to product
This publication forms part of the Open University module TM354 Software engineering. Details of this and other Open University
modules can be obtained from the Student Registration and Enquiry Service, The Open University, PO Box 197, Milton Keynes MK7
6BJ, United Kingdom (tel. +44 (0)845 300 60 90; email general-enquiries@open.ac.uk).
Alternatively, you may visit the Open University website at www.open.ac.uk where you can learn more about the wide range of
modules and packs offered at all levels by The Open University.
To purchase a selection of Open University materials visit www.ouw.co.uk, or contact Open University Worldwide, Walton Hall,
Milton Keynes MK7 6AA, United Kingdom for a catalogue (tel. +44 (0)1908 858779; fax +44 (0)1908 858787; email ouw-customer-
services@open.ac.uk).

The Open University, Walton Hall, Milton Keynes MK7 6AA


First published 2014.
Copyright © 2014 The Open University
All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, transmitted or utilised in any form or
by any means, electronic, mechanical, photocopying, recording or otherwise, without written permission from the publisher or a
licence from the Copyright Licensing Agency Ltd. Details of such licences (for reprographic reproduction) may be obtained from the
Copyright Licensing Agency Ltd, Saffron House, 6–10 Kirby Street, London EC1N 8TS (website www.cla.co.uk).
Open University materials may also be made available in electronic formats for use by students of the University. All rights, including
copyright and related rights and database rights, in electronic materials and their contents are owned by or licensed to The Open
University, or otherwise used by The Open University as permitted by applicable law.
In using electronic materials and their contents you agree that your use will be solely for the purposes of following an Open University
course of study or otherwise as licensed by The Open University or its assigns.
Except as permitted above you undertake not to copy, store in any medium (including electronic storage or use in a website),
distribute, transmit or retransmit, broadcast, modify or show in public such electronic materials in whole or in part without the prior
written consent of The Open University or in accordance with the Copyright, Designs and Patents Act 1988.
Edited and designed by The Open University.
Printed and bound in the United Kingdom by Martins the Printers, Berwick-upon-Tweed.

ISBN 978 1 7800 7917 2


1.1
Contents
Unit 9
Architecture, patterns and reuse 1
Unit 10
Building blocks and enterprise architectures 53
Unit 11
Product quality: verification, metrics and testing 99
Unit 12
The case study: part 3 157
Acknowledgements 207
Index 209
Unit 9 Architecture, patterns and
reuse
Contents
1 Introduction 5
2 Architecture 6
2.1 What is software architecture? 6
2.2 The twin peaks of architecture and requirements 9
2.3 Can architecture and agile live together? 11
2.4 Requirements and architectural decisions 12
2.5 Architectural views 14
2.6 Stakeholders, views and viewpoints 15
2.7 Summary of section 17
3 Reuse 19
3.1 Reuse on different levels 19
3.2 Summary of section 20
4 Reusing architecture 21
4.1 Architectural styles 21
4.2 Frameworks 27
4.3 Product lines 28
4.4 Summary of section 30
5 Reusing design 31
5.1 Adapter design pattern 31
5.2 Interfaces 33
5.3 Codifying patterns 34
5.4 Model-view-controller pattern 36
5.5 Observer pattern 40
5.6 Singleton and Factory patterns 43
5.7 Summary of section 50
6 Summary 51
References 52
1 Introduction

Introduction 1

Figure 1 The British Museum

This unit looks at two central principles of all engineering disciplines:


structure and reuse.
First we look at software architecture, which is concerned with software
elements, their externally visible properties (in other words their interfaces in
the widest sense) and their relationships (which include their interactions).
We then examine how architecture interacts with requirements and we shall
see that architecture and requirements evolve together and are closely
intertwined. A particular subset of requirements, which we describe as
architecturally significant requirements (ASRs), has a major influence on the
architecture of a system. We look at how different views can be used to
represent the system – focusing on the logical, process and deployment views
– and how models and conventions are used to describe these views.
An important goal of software engineering is reuse. We look at architectural
styles, which represent basic forms of architecture we can reuse in the design
of new systems. Frameworks and product lines provide complete architectures
designed for reuse.
Architecture is only one part of the design of a software system. The other
part of the design is the internal structure of the software elements
themselves. Software patterns represent reusable solutions to common design
problems at this level. We discuss a number of important patterns and give
examples of their use.

5
Unit 9 Architecture, patterns and reuse

2 Architecture

You don’t need architecture to build a dog kennel, but you’d better have
some for a skyscraper.
Booch, 2000

In this section we look at software architecture and how it interacts with


requirements.
You will learn that architecture and requirements should evolve iteratively
and in parallel, and that non-functional requirements are the main drivers of
architectural decisions.

2.1 What is software architecture?


In Block 1 Unit 1 software architecture was defined as follows.

The software architecture of a program or computing system is the


structure or structures of the system, which comprise software elements, the
externally visible properties of those elements, and the relationships among
them.

To get a feeling for what this means, at least in terms of elements and how
they fit into a structure and interact with one another, let’s look at some
examples.

Example 1
A popular architecture for websites is the LAMP stack. The acronym LAMP
refers to the use of four open-source technologies:
. Linux
. Apache webserver
. MySQL database
. PHP (or Python or Perl – all programming languages).
The structure is shown in Figure 2.

web server

client database
PHP

web browser Linux, Apache, PHP MySQL

Figure 2 LAMP stack

6
2 Architecture

Variants on this architecture may replace Linux with other operating systems,
for example Microsoft Windows (a WAMP) or Mac OS (a MAMP).
There are various things to notice about Figure 2. It shows in fact a mixture
of architectures: a client–server architecture, with the web browser client
sending a request to a web server and receiving a response; and a LAMP
stack platform. It shows also a layered architecture. There are three layers,
corresponding to the client, the web server that constructs the response and
the database storage.
This illustrates that a particular system may show a mixture of architectural
styles. Architectural styles will be the subject of subsection 4.1.
The Wikimedia architecture is based on LAMP, but has been modified in
various ways, most of which are to do with performance – demonstrating
how a non-functional requirement such as performance can have a major
effect on architecture.

Example 2
Figure 3 shows the very different architecture used by Skype, a well-known
internet telephony service.

login
server

user 1

user 2
super
node 3
super
node 2 user 3

super
node 4
super
node 1 user 4

user 6 user 5

Figure 3 Skype architecture

The small circles – user 1, user 2, and so on – represent individual users. To


go online a user must first contact a login server (dotted line from user 1)
and is then connected to the nearest of a series of interconnected hubs called
super nodes (super node 3 in user 1’s case). The super nodes then route

7
Unit 9 Architecture, patterns and reuse

telephone calls from one user to another, e.g. user 2 to user 4 via super nodes
3 and 4. It is also possible for two users to be connected directly (e.g. user 5
and user 6).
Unlike the open-source products used by the LAMP stack, Skype’s software
is all proprietary. Skype users must download and use the software Skype
provides and cannot use alternative software from third parties.
Originally all the nodes except the login server were equivalent to one
another. Any suitable node could be promoted to a super node and become
responsible for routing calls. This style of architecture, in which there is no
clear distinction between clients and servers, is known as peer-to-peer (P2P).
Subsequently it was decided to move the super nodes to Skype’s own servers
in order to improve reliability. This move was completed in 2012, so that
Skype now has a mixed P2P and client–server architecture. This is another
example of how a non-functional requirement, reliability in this case, can
have a major effect on architecture.

The two examples above both involved large-scale applications running over
networks but all applications – large and small – have an architecture of
some form. The next example is a simple application that runs on an
individual machine.

Example 3
The Windows command prompt supports a number of commands that allow
us to process data in a variety of ways. Three particular commands are

findstr/b Find a string at the beginning


findstr/e Find a string at the end
sort Sort alphabetically

These commands are filters: they take some data and process it to produce a
result. The findstr/b command finds all the lines in a file that begin with a
given string. For example, if elements.txt is a file containing a list of all the
chemical elements then
findstr/b "P" elements.txt

will produce a list of all the elements that begin with ‘P’ (Figure 4).

elements.txt filter lines beginning


with "P"
findstr/b "P"

Figure 4 Filter

The output from one filter can be ‘piped’ to another filter. The pipe is
visualised as a conduit along which the data flows. The two filters each

8
2 Architecture

perform an independent chunk of processing and are not aware of one


another – they just perform a particular action and then the data is piped
onwards. The Windows command for a pipe is | and the following
combination will pipe the output from findstr/b to findstr/e.
findstr/b "P" elements.txt | findstr/e "um"

Taking the result of the first filter (lines beginning with ‘P’) it will find
among those the lines ending with ‘um’. There is no need to stop here: the
result of the second filter can be piped to a third. This time it sorts the
elements that begin with ‘P’ and end with ‘um’ (see Figure 5).
findstr/b "P" elements.txt | findstr/e "um" | sort

elements.txt filter pipe filter pipe filter


Palladium
findstr/b "P" findstr/e "um" sort Platinum
Plutonium

Figure 5 Pipes and filters

This is an example of a particular form of architecture called pipe-and-filter


or data flow, which we will meet again in Section 4. You may also find
references to pipe-and-filter as design or architectural pattern – we will be
discussing patterns in Section 5 but the distinction between an architecture
and a pattern is not always clear.

In Block 1 Unit 1 you learned that a choice of architecture is an early


decision that a development team must take and that the choice will affect
many important concerns that stakeholders have. The architecture will also
act as an overarching guide for the developers. This raises two questions.
. How far do requirements need to be known before architectural choices
can be made?
. Does making an early decision about architecture conflict with an agile
and iterative approach?
These issues are related and we explore them in the next two subsections.

2.2 The twin peaks of architecture and


requirements
We are searching for some kind of harmony between two intangibles: a
form which we have not yet designed and a context which we cannot
properly describe.
Alexander, 1964

We’ve stated that architecture must be considered early in the development


process. You learned in Block 1 Unit 1 that building a view of the system’s
architecture in early iterations is a Unified Process (UP) best practice.

9
Unit 9 Architecture, patterns and reuse

SAQ 1

Suggest a reason why choosing the architecture very late on might be a


bad idea.
Answer

Choosing the architecture at a very late stage suffers from the risks of the
waterfall approach. By then many other design decisions will have been
taken and it will be too late to change them, even if they force us to adopt
an architecture that is less than ideal.
An analogy could be with building a house. Would anyone dream of
starting construction without an architectural design for the building?

You can imagine some aspects of a system’s architecture being among the
first things considered by a developer.
For example, if you are told that a system is required for handling room
reservations and allocations, you would naturally have questions. Questions
such as whether it’s just one hotel or several, how many rooms are involved,
will the user interface be web based or desktop or both, can customers use it
or just hotel staff, will there be multiple simultaneous users, and so on.
The answers to these questions mould some of the architecture of the system
that is eventually developed. For instance, if you are told that customers must
be able use the system via a web-based interface then you know some form
of web server will be needed, perhaps resembling the LAMP stack described
in Example 1. Or to take another illustration, if you learn that only one hotel
is involved and all the administration is carried out from a single desktop
machine then that suggests the system does not need to run over a network.
However knowing something about the architecture alone is obviously not
sufficient for you to start designing the system, because you don’t yet know
what the system must be able to do and what the needs of its users are – the
functional and non-functional requirements. So gathering requirements will
also begin right at the start of the development process.
Some aspects of the architecture are dictated from the start by considerations
such as the purpose of the system and the business environment it will
operate in. However as the design progresses you will need to make design
choices and these will in turn influence the proposed architecture. As you will
see shortly, these are often driven by non-functional requirements.
If developers choose to fix on either requirements or architecture as their
starting point they will be following what is at least partially a waterfall
model, with the associated risks. Whichever way they go there is a high
probability that wrong decisions will be taken but the problems will not
emerge until it is too late to change.
The solution to this dilemma is to recognise explicitly that requirements and
architecture are inextricably linked, and to consider them in parallel.

10
2 Architecture

This is the thinking behind the twin-peaks model (Nuseibeh, 2001), which
develops requirements and architecture concurrently and iteratively (see
Figure 6).

Low Specification

Level
of
detail

High
Requirements Architecture

Figure 6 Twin-peaks model (adapted from Nuseibeh, 2001)


The twin-peaks model gives equal prominence to requirements and
architecture and uses an extension of the spiral process you met in Block 1
Unit 1. As you descend the peaks you consider alternately requirements and
architecture, intertwining the two and with each successive iteration
specifying both in greater detail.
This twin-peaks model has been very influential and at the time of writing
(2013) is the object of a great deal of ongoing research.

2.3 Can architecture and agile live together?


A lot of attention has rightly been given to the question of whether there is a
conflict between architecture and agility.
After all, an architecturally focused approach will start to consider very early
on what the main elements of the system are and how they will interact. In
contrast, an agile approach will typically emphasise being adaptive and
delaying decisions for as long as possible.
In fact some agile practitioners may take the view that there is no need to
pay any special attention to architecture, as it will emerge naturally as a
result of addressing requirements in a series of iterated code development
cycles. They may prefer to avoid explicit architectural decisions in case they
limit the flexibility of developers to respond to changes in user requirements.
Agile practitioners may also fear that architecture is associated with ‘big
design up front’ (BDUF), which limits flexibility and runs against the
lightweight development ethos of agile.
However our view is that architecture and agility are far from incompatible
and in fact can benefit one another.
The twin-peaks model allows architecture to evolve iteratively in a series of
small increments, so there is no risk that a BDUF will emerge. Because
development is fine-grained it can be highly responsive to change. By giving
architecture equal emphasis with requirements, twin-peaks also helps to
answer the criticism often made that agile does not scale up to larger

11
Unit 9 Architecture, patterns and reuse

projects. Moreover the interweaving of requirements and architecture allows


customers to become involved in architectural decisions.
These views are supported by a growing consensus. In a recent survey,
Falessi et al. (2010) found that many agile practitioners consider architecture
to be important for complex projects and that agile values and architecture
are mutually supportive. Architecture is seen as contributing in a variety of
ways, such as:
. aiding communication
. documenting assumptions about the system
. feeding into subsequent development cycles.

2.4 Requirements and architectural decisions


Non-functional requirements
A brief thought experiment shows that there can be various architectures
capable of meeting a set of purely functional requirements.
As an everyday analogy suppose I want to travel from A to B. There may be
many possible ways to fulfil this: walk, drive, bus, train and so on. But some
of these might not meet other requirements I have, such as comfort, cost or
time of arrival.
The following example shows how similar considerations can apply to
software applications.

Example 4
Consider a word-processing system. The functionality – creating, formatting,
editing and saving documents and so on – is often provided by a program
running on the user’s local machine, which is also where the documents are
stored.
Alternatively all the processing and storage can be handled on a remote
server – ‘in the cloud’ – with the user’s computer just running a browser that
connects to the remote server over the internet.
Both these solutions may offer exactly the same facilities as far as word
processing goes.
But they will not necessarily be equivalent in terms of non-functional
requirements, such as security and reliability, and the differences are directly
related to the architecture – documents might be intercepted as they travel
across the internet, which affects security, and having to rely on a remote
server and an internet connection is likely to reduce reliability.

So there can be multiple architectures that meet the functional requirements


but they will not all be equal when it comes to non-functional requirements.
Figure 7 illustrates that meeting non-functional requirements is a stronger
restriction than meeting functional ones. Moreover not all non-functional

12
2 Architecture

requirements will be of equal importance when making architectural


decisions.

Architectures that also meet non- Architectures that meet


functional requirements functional requirements

Figure 7 Meeting non-functional requirements is more restrictive

Architecturally significant requirements


Chen et al. (2012) interviewed 90 professionals with between them 1448
years’ experience of working with software architecture across a wide range
of organisations. From the responses they were able to pick out
characteristics that can be used at the requirements gathering stage to help
identify what they describe as ‘architecturally significant requirements’
(ASRs) – the requirements that drive a system’s software architecture. The
four characteristics they identified were as follows.
. Quality attributes are non-functional requirements such as security,
reliability, availability, usability, maintainability, portability and so on that
you have met in earlier units. In Unit 10 we shall look in some detail at
how these quality attributes (often called ilities) drive architectural
decisions.
. Core features are described by Chen et al as ‘the problem the software is
trying to solve’. For example, an online chat application is intended to
allow participants in different locations to exchange text messages. To do
this a network of some sort is essential, which immediately has
implications for the architecture.
. Constraints. All requirements can be described as constraints, but here the
authors mean requirements such as technical constraints – for example,
the client may have specified a particular programming language – or
non-technical constraints such as budget or time.
. Application environment. This is the environment in which the system will
run. For example, a navigation app will need GPS connectivity.
Some overlap is possible but in general these represent a very effective
checklist that developers can use to recognise ASRs.

13
Unit 9 Architecture, patterns and reuse

Exercise 1
One of the professionals Chen et al. interviewed provided an unusual quality
attribute, ‘bettability’, which related to a betting website’s attractiveness to
potential customers.
Think of a similar non-standard quality attribute that could be applied to
some imaginary website.
Solution
There are many possibilities. One we thought of was ‘likeability’. There are
obviously many websites and apps that aim to inspire affection among
visitors.
Another was ‘sustainability’. Different architectures will have different
implications for energy consumption and this is of increasing importance.
Others we came up with were ‘extensibility’, a desirable quality of wikis, and
‘customisability’ for those who want to tailor a website’s appearance to their
own preference. ‘Readability’ is another one, as well as ‘findability’ (will it
show up in famous search engines?).

2.5 Architectural views


Like other aspects of software design, software architectures are described
using models. However software architectures are usually too complex to
describe using a single type of model. Instead we use a series of different
architectural views. Each view tends to emphasise issues that matter to
particular groups of stakeholders.
Many writers have explored what would be a good set of views to use. In an
influential paper Kruchten (1995) introduced a ‘4+1 model’ that was later
incorporated into the UP (Kruchten, 2004). He proposed the following
architectural views.
. The logical (or functional) view describes the system’s main functional
elements and their interactions – broadly, the services the system must
provide to users.
. The process view describes the set of independently executing processes
that will exist at run-time and the communication between them.
. The physical (or deployment) view describes how the system will be
deployed to an operating environment in terms of the physical computers
and networks on which the system will run.
. The development view describes how the software will be split into
subsystems that can be allocated to teams of developers.
The ‘+1’ part of the model is the use of key scenarios – instances of use
cases – to help develop and validate the design.
Bass et al. (2003) present a slightly different set, although they are essentially
equivalent to Kruchten’s.

14
2 Architecture

. The module view corresponds to the logical view in the 4+1 model. It
describes the main functional responsibilities of the system and how the
different parts depend on one another.
. The component and connector view is similar to the process view in the 4
+1 model, with the independent processes being regarded as components
and the communications between them modelled by connectors.
. The allocation view describes all the mappings between the software and
the external environment in the widest sense and roughly corresponds to a
combination of the physical and development view in the 4+1 model.
There are a number of other well-known view sets but these are the only two
we will consider. We won’t follow either of them exactly, instead using a set
loosely based on the 4+1 model but drawing in some elements from Bass
et al. This will fit in better with the ideas we are trying to emphasise in
TM354. Our view set comprises the following.
. The logical view describes the main functional elements and how they
interact, and assumes that these correspond to subsystems that can be
allocated to teams of developers. This is effectively an amalgamation of
Kruchten’s logical and development views. We made this choice because
we don’t plan to explore in any detail how work is allocated to teams so
there is no need for a separate view.
. The process view describes the independent processes executing at run-
time and the communication between them, regarding the processes as
components and the communication as taking place via connectors. This
is similar to the process view in the 4+1 model but using the component
and connector terminology borrowed from Bass et al. We made this
choice because components and the communication between them is an
important part of the discussions in this unit and the next.
. The deployment view is the physical view in the 4+1 model, describing
how the system will be deployed to an operating environment, the
physical computers and networks on which the system will run. It is
important to us because deployment decisions will have a major impact
on quality attributes.
You will see these views used in Unit 12, where we develop a first iteration
for part of the hotel system.

2.6 Stakeholders, views and viewpoints


Stakeholder concerns were briefly discussed in Block 1 Unit 1. A system will
have many groups of stakeholders and each stakeholder group will have a
particular set of concerns. A concern is some aspect of the system that is of
crucial importance from the point of view of one or more groups of
stakeholders. Concerns can be to do with the system’s functionality or with
qualities such as correctness, performance, security and so on. The following
exercise explores the possible concerns of some different stakeholder groups.

15
Unit 9 Architecture, patterns and reuse

Exercise 2
Imagine a large distance-learning university requires a system for the
electronic submission of student assignments, their subsequent marking and
return, and the recording of results.
Think of some stakeholder groups and for each group suggest one or more
concerns that might influence the architecture.
Solution
We thought of these examples but you may have thought of others. After
each we’ve indicated what category or categories of ASR it falls into.
. Students and teaching staff will be concerned with core functionality,
usability, availability and performance – core features and quality
attributes.
. The examinations section will be particularly concerned with security –
quality attributes.
. The finance department will be concerned with the cost – constraints.
. Management will be concerned with the cost, when the system will be
delivered and its effectiveness – constraints, core features.
. Designers will be concerned with how the system can be partitioned, what
the run-time elements will be and where they will be deployed – the
application environment.
. Programmers will be concerned with the core functionality represented in
the design, how easily the design can be implemented in code and what
language(s) will be used – core features, quality attributes, constraints.
. Testers will be concerned with whether the system is easy to test – quality
attributes.
. The IT department will be concerned with the operating environment and
how easy the system is to run – application environment.
. Developers responsible for maintaining the system will be concerned with
how easy the system is to modify – quality attributes.

In the previous subsection you learned that an architecture is too complex to


be expressed in a single all-embracing description. Instead you require a
number of views, each of which addresses a particular set of stakeholder
concerns. The views should be intelligible to stakeholders and should allow
them to see how the architecture deals with their concerns and how the
architect has balanced the claims of competing concerns by suitable
compromises.
For each type of view there are a particular set of conventions and models
you can use to document it. For example, when documenting a logical view
you will use structural models and package diagrams. Documenting a process
view will involve describing the components and the connectors that they use

16
2 Architecture

to communicate. A deployment view can be represented using a deployment


model.
We can draw a loose analogy here with the architecture of a physical
building. Describing the architecture requires many models of different types
and each will be represented by certain types of diagrams with a particular
set of symbols and conventions – a language. For example, there will be floor
plans and elevations, which are of importance to the client, the architects and
those who will erect the shell of the building. The electricians will require
architectural wiring diagrams that show the layout of the electrical services.
Still other sets of models will be needed to describe the plumbing, and so on.
In software architecture the set of conventions and models appropriate to a
particular view is often referred to as a viewpoint. You can think of a
viewpoint as the language that is used to document a specific type of view.
These ideas are included in an important standard, ISO/IEC/IEEE
42010:2011, Systems and software engineering — Architecture description,
which presents a conceptual framework for architectural descriptions, a
simplified version of which is shown in the UML diagram of Figure 8. Note
the aggregation diamonds, which express the fact that the architectural
description consists of a set of views and viewpoints.

1 1..* 1..* 1..*


System Stakeholder Concern
1..*
1 1..* 1..* 1..*

1 1
1 Architecture
Description

1..* 1..* 1..* 1..*


1 1
Viewpoint View

Figure 8 Conceptual framework for architectural descriptions

2.7 Summary of section


In this section you first revisited the definition of a software architecture and
then saw several examples of different architectures.
Requirements and architecture go hand in hand and both must be considered
in parallel right from the start. Following an iterative twin-peaks model of
development allows architecture to be responsive to change and to provide
support for agile development in ways such as aiding communication,
documenting assumptions and contributing to subsequent iterations.
Non-functional requirements shape architecture, along with other
considerations such as the system’s core purpose and the environment it will

17
Unit 9 Architecture, patterns and reuse

run in. Collectively these factors are known as architecturally significant


requirements (ASRs).
An architectural view is a representation of the system that emphasises the
concerns of a particular set of stakeholder groups. The description of an
architecture requires several such views because no one view can capture the
concerns of all the stakeholders.
A range of view sets have been proposed. The version used in this module
consists of the logical, process and deployment views.
Each architectural view has a particular set of models and conventions that
are used to describe it. This is often called a viewpoint.

18
3 Reuse

Reuse 3
A major aspiration of software engineering is reuse – taking what you or
others have done or created in the past and using it either unchanged or with
relatively little adaptation. We discussed reuse at the beginning of this
module and will focus, in the next sections, on how it can be exploited in
architecture and in design.

SAQ 2

Think of some reasons why reuse is desirable.


Answer

We thought of several reasons. You may have come up with others.


. It avoids duplication of effort and reinventing already existing
solutions, which saves resources.
. It promotes reliability because developers can use tried and trusted
solutions.
. It speeds up development because developers can take existing
solutions without starting from the beginning every time.
. It is a mechanism for spreading good practice among the software
development community and familiarising practitioners with tried
and tested solutions.

3.1 Reuse on different levels


Reuse can involve software components – replaceable and reusable software
elements, as defined in Block 1 Unit 1. However it can also involve reuse of:
. knowledge in the elicitation of requirements, known as requirements
patterns
. ways of solving conceptual analysis problems, known as analysis
patterns
. architecture – this may just involve reusing a structural idea or it may go
further and allow the creation of a series of systems by taking the same
basic architecture and modifying the detailed code
. ways of solving particular problems of design and implementation, known
as design patterns
. ways of doing things in a particular language – these are small units of
reuse known as idioms.
The order in the list above roughly follows the spiral path down the twin
peaks, going from earlier iterations to later ones, from larger units of reuse to
smaller ones, and from requirements to code.

19
Unit 9 Architecture, patterns and reuse

These different forms of reuse all represent solutions that developers have
found to problems in the past and that can be described and catalogued so
that others can take advantage of the knowledge. What is being reused is not
the exact details of the design or code, but the ideas involved in the solution.
You might like to think in terms of templates, which are patterns that can be
used as a guide when you meet a new problem that resembles an old one.
What is the characteristic theme that you recognise again and how was the
problem solved successfully in the past?

3.2 Summary of section


In this short section you saw that as well as reusing software components an
important aspect of software engineering is the reuse of ideas and patterns
that have proved successful in the past. This can take place on a range of
levels, from architecture down to ways of doing things in a particular
language, and at different stages, from requirements through to analysis and
design.

20
4 Reusing architecture

Reusing architecture 4
4.1 Architectural styles
At a very general level we can identify a number of basic plans used in
software architecture; these have come to be known as architectural styles.
Architectural styles, which form part of the process viewpoint, were first
described by Shaw and Garlan (1996). The software can be viewed as a
system of components, perhaps of different types, that interact via connectors.
The various architectural styles are aimed at solving particular system design
problems and each one represents a particular reusable pattern of components
and connectors. What distinguishes the styles is the type of components and
what kinds of connector are used.
Commonly architectural styles include:
Client–server
Call-return
Layered
Peer-to-peer
Data flow
Data-centred
Independent components
Service-oriented
Notification
You should recognise some of them from the examples we looked at in
Section 2. By combining these basic forms it is possible to build up more
complex architectures and most systems will display aspects of more than
one style. It is also important to note that there are relationships between
different styles and some overlap with others.
Below we describe each of these styles in more detail.

Client–server
Client–server style is probably the best known of all architectural styles.
One component (the server) provides a service to the other component (the
client). The server waits for requests from clients, processes each one as it as
received, and returns a response to the client.
A familiar example of the client–server style is a request sent from a web
browser to a web server. You saw an example of this in Example 1 of
Section 2.
The components are programs and the connector is the request initiated by
the client. Client and server may be on the same machine or connect via a
network. Typically there are many clients for each server.

21
Unit 9 Architecture, patterns and reuse

The client–server style is illustrated in Figure 9.

request
client
response

client server

client

Figure 9 Client–server architectural style

Typically the client and the server are only loosely coupled. The next style
we consider is a specialised variant in which coupling is somewhat tighter.

Call-return
In a call-return style a component (the caller) makes a procedure call to
another component (often known as the callee) and waits for the call to
return. In traditional software a main program calls a subprogram and then
waits for a reply. In object-oriented programming the call takes the form of a
method invocation by one object on another by the sending of a message.
The components are programs or objects and the connector is the message
sent. The caller and callee can be on different computers and the call made
over a network. Usually the complete system will include many callers and
callees and the communication channels that carry the messages are fixed.
The call-return style is illustrated in Figure 10.

caller callee

Figure 10 Call-return architectural style

Layered
Layers were introduced in Block 1 Unit 1 and you saw an example in
Section 2 (the LAMP stack in Example 1). The essence of a layered style is
that the system is structured as a series of layers that are visualised as

22
4 Reusing architecture

stacked on top of another. Each layer uses services provided by the layers
below (usually just by the layer immediately below). It also supplies services
to the layer above.
An example of a layered architecture is a compiled Java program, which
executes in a Java virtual machine that in turn makes calls to services
supplied by the operating system. Another example is the familiar client–
server architecture, in which there are just the two layers and the connector
takes the form of a request and response, often over a network.
The components in this style are the various services in each layer and the
connectors are the calls made on the services. Normally a layer can only
communicate with layers above and below. There is no communication
between components in the same layer.
The layered style is illustrated in Figure 11.

top layer services


...

layer n+1 services

layer n services

layer n+1 services


...

bottom layer services

Figure 11 Layered architectural style

Peer-to-peer
The peer-to-peer style resembles the client–server style except that all the
components are both clients and servers and any component can request
services from any other component. An example of peer-to-peer is a
streaming music service, where listeners generally get streamed tracks from
the nearest peer that can be located by sending a request that hops from one
peer to another until the desired track is located.
In the simplest form all the components are identical and the links are short-
lived ones set up to deal with each particular request.
The peer-to-peer style is illustrated in Figure 12.

23
Unit 9 Architecture, patterns and reuse

peer

peer peer

peer peer

Figure 12 Peer-to-peer architectural style

Data flow (also known as pipes and filters)


As you saw in Example 3, in the data-flow style components are objects or
small independent subprograms (filters) that process a stream of data and pass
the results on to other components for further processing. Communication is
unidirectional and uses fixed channels. Each filter has no knowledge of other
filters upstream or downstream, but simply accepts the data, processes it and
passes it on. The connectors are services, provided by the operating
environment, that ‘pipe’ data from one filter to another.
This type of architecture is commonly used in the Unix operating system for
combining functions and is also seen in the Stream application programming
interface (API) introduced from Java 8 onwards. The data-flow style is
illustrated in Figure 13.

pipe pipe pipe pipe


filter filter filter

Figure 13 Data-flow architectural style

Data-centred
In the data-centred style there is a data provider that is a centralised store of
persistent data. The structure of the data, the types of items and their
relationships are stable and change rarely or not at all. There are many clients
who are data consumers. Items can be created, queried, updated and
destroyed on request. The central store may be duplicated, to provide backup
in case of failure or to deal with a greater volume of client requests. The
communication channels are normally fixed.
There are two forms of the data-centred style (see Figures 14(a) and (b)):
. If communication is always initiated by clients and the store simply
responds to requests it is called database or repository. Typically the
components are a database server and clients that access it. The

24
4 Reusing architecture

connectors are database queries made via a special database connection. A


database holding personnel records is an example of this form, with
authorised users being able to log on and submit queries.
. A variant in which the store is active and informs users of changes, so
that communication may be initiated from either end, is termed a
blackboard.

database client

client data

client
(a)

notify

notify
blackboard client

client data

notify
client
(b)

Figure 14 Data-centred architectural style, (a) database, (b) blackboard

Independent components
In the independent components style, components execute concurrently and
are decoupled as far as possible, but can communicate by messages that
allow them to exchange data or coordinate their operations. The connectors
are the message exchange protocols, which are normally asynchronous – that
is, the sender can continue without waiting for an answer from the other
component.
For example, a set of components might control different parts of a chemical
processing plant, independently regulating the part each is responsible for, but
sharing data and coordinating with one another by exchanging messages.

25
Unit 9 Architecture, patterns and reuse

Figure 15 illustrates an example of the independent components style.

messages

process process

messages messages
messages

process process
messages

Figure 15 Independent components architectural style

Service-oriented
In the service-oriented style there are two kinds of component, the
consumers and the providers – a set of service providers makes services
available to a set of service consumers. Consumers can combine services in
order to carry out the business processes they require.
The connectors are the requests and responses sent between consumers and
providers, using standard communication protocols. In some cases
communication is facilitated by a virtual communication channel called an
enterprise service bus (ESB), which supports features such as service look-up
and routing of service requests.
An example could be different divisions of an organisation whose systems all
use a common set of services such as payroll, personnel, customer records
management billing and so on.
Figure 16 illustrates a service-oriented style using an ESB.

consumer consumer consumer

enterprise service bus

provider provider

Figure 16 Service-oriented architectural style with ESB

26
4 Reusing architecture

Notification (also known as implicit invocation or publish–


subscribe)
In the notification style the two kinds of components are observers and
subjects. Observers can register themselves with a subject in order to be kept
notified whenever some particular event happens at the subject’s end.
At an architectural level this style is usually referred to as publish–
subscribe. Subscribers register to receive updates (often messages)
whenever a publisher posts a new item, for example an RSS feed where
anyone who signs up to the feed receives news updates as they become
available. Notification also appears at a more detailed design level. In
subsection 5.5 we will discuss a form of publish–subscribe that deals with
objects rather than distributed components and is known as the Observer
design pattern.
Another example of notification is an event-based model such as Java Swing,
in which components can register themselves with other components to
receive information about events such as user input and react to them by
executing appropriate event-handling code.
In publish–subscribe there tend to be many subscribers for each publisher.
The connectors are either procedure calls or messages transmitted via
intermediate software such as a messaging system or a run-time system like
the Java virtual machine.
Figure 17 illustrates the publish–subscribe form of the notification style.

register subscriber
register
publisher
update
subscriber
new
item
register
update

subscriber
update

Figure 17 Publish–subscribe architectural style

4.2 Frameworks
Very often a system will have an architecture very similar to that of systems
developed in the past. It may be possible to reuse a large part of the
architecture, along with any code the systems have in common. Segments of
architecture and code that can be reused in this way are usually called
frameworks.
Some code is provided for the framework itself, along with code for
components that a developer can adapt to the needs of the new system and

27
Unit 9 Architecture, patterns and reuse

slot into the architecture of the framework. The framework is accompanied


by documentation giving the details of how it is used.
An example is the Java Swing framework for constructing graphical user
interfaces. To define the graphical components of the interface – windows,
menus, buttons and so on – the developer extends classes that form part of
the framework.
Swing uses an event-based model in which event handlers, again written by
the developer but based on the software provided by the framework, allow
the interface to respond to user events such as mouse clicks. Connections
between the graphical components and the event handlers are created using
the facilities of the framework.

4.3 Product lines


Often a company wants to produce many similar software systems.
For example, engines in motor cars are controlled by an on-board computer
that uses quite sophisticated software. Different engines and cars require
slightly different engine-control software, but all engine controllers are
essentially the same.
A manufacturer of engine-control software can produce a series of engine
controllers for different customers, reusing major parts of the software from
customer to customer, with only those parts peculiar to the particular
customer being different. Using the jargon of engineering, this is known as a
product line, a line of products that differ only in detail.
Developing product lines for software began in the mid-1980s, mostly in the
defence sector where the costs and reliability of routine weapons systems
were a cause for concern. Trials were made on rocket guidance systems. The
methods moved from defence to the civil sector and by the late 1990s the
practice of product lines had become established for normal commercial
systems as well.

The product-line process


A software product line is essentially a domain-specific framework. Building
and using a software product line follows a series of stages.
1 Product-line initiation: the first stage is to recognise that a series of
related software systems do not differ much from each other. This leads to
domain analysis.
2 Domain analysis: existing systems in the domain are examined and
domain experts are consulted. The area in which the product line will
operate is thoroughly analysed to obtain the general customer needs and
the terminology normally used, leading on to general models of data and
process, which are in effect the conceptual class diagram and use cases.
3 Architecture specification: further analysis leads to the architecture for the
product line. There may be several such reference architectures produced.
These are further elaborations of the domain model that add detail,

28
4 Reusing architecture

particularly about the prospective implementation, but still keeping


flexibility in mind.
4 Component collection: in the same way as a framework, each of the
reference architectures will be accompanied by a repository of
components that can be used with it, together with the associated
documentation.
These four stages, which are typically very complex and difficult, establish
the product line from which products can be developed that are tailored to
the needs of individual customers. For each particular product a further four
stages are needed.
5 Specific-requirements capture: specific requirements for the new product
are captured as specialisations and extensions of the domain model
developed in Stage 2.
6 Architecture specialisation: a reference architecture is selected on the
basis of the specific requirements. Changes are made to accommodate the
specialisations and extensions of the domain model corresponding to the
new requirements.
7 Component selection and specialisation: the product that meets the new
requirements is then built, selecting components from the repository where
these exist and adapting them as necessary.
If there is nothing appropriate in the repository, a new component may be
built by those managing the repository for the product line. Alternatively
if a component is too specialised to be reusable in other applications it
may be simply be written as a one-off.
Some ‘glue code’ that adapts new components to the framework may also
need developing.
8 Integration and release: finally the components must be integrated into the
architecture – a relatively easy process if the architecture has been well
designed.
An example of a software product line is syngo.via developed by Siemens
Healthcare Sector (Software Product Line Conference, 2012). The products
are a family of applications that help healthcare professionals to reach
diagnoses on the basis of a range of body imaging systems, such as
computerised tomography (CT) and magnetic resonance imaging (MRI).
Between 2009 and 2012 more than 70 applications had been developed using
this product line.

SAQ 3

What are the various ingredients that make up a framework?


Answer

A framework consists of an architecture, a small amount of software for


the framework, a set of components suitable for use within the framework
and the documentation needed to make use of the framework.

29
Unit 9 Architecture, patterns and reuse

SAQ 4

List the various forms of reuse discussed in Section 4 and say briefly what
is reused in each case.
Answer

Architectural styles reuse expertise in large-scale architectural design – the


types of component used and the patterns of interaction between them.
Frameworks reuse a particular architecture and a set of software
components suitable for use within it.
Product lines reuse reference architectures, software components and
expertise about the variations needed to fulfil customer requirements.

4.4 Summary of section


In this section you were introduced to a range of common architectural styles
– basic plans that an architecture can follow. Styles are characterised in terms
of the types of component involved and the forms of communication between
them. They can be combined to give more complex architectures and most
systems involve the use of more than one style.
A framework consists of some architecture, code for software components
that slot into the architecture and the documentation needed to use the
framework. By adapting the components as appropriate it is possible to
develop many different systems that reuse the basic architecture and code the
framework provides.
A company will often want to produce a range of very similar software
systems. It is often possible to create a domain-specific framework called a
software product line, which reuses not only architecture and code but also
expertise about how to fulfil the requirements of different customers.

30
5 Reusing design

Reusing design 5
5.1 Adapter design pattern
In Section 4 we looked at the reuse of architectures. Here we look at design
patterns, which represent reusable solutions to design problems at a more
detailed level. Usually design patterns only apply in an object-oriented setting
and involve just a few software classes and the relationships between them.
Each pattern solves a recognisable problem that crops up over and over
again. To understand a design pattern you must first understand the problem
it is trying to solve.
We’ll begin with the design pattern called Adapter. A very common situation
is to have a client that expects to make calls to a particular interface but then
find it needs to work with a class that has a different interface. For example,
it might be a legacy class, one originally developed for a different project or
a component bought in from a third party.
This problem is essentially the same as the real-life one faced by a traveller
who takes an electrical device to a place where it won’t plug in to the local
power supply. The solution is an adapter which sits between the device and
the supply (see Figure 18). The adapter plugs into the local supply and the
device plugs into the adapter.
To understand the software equivalent of this consider the following example.
Suppose you are developing a very simple drawing package. Your initial
design has a client and a class Square with an operation draw(). When draw()
is invoked a square will draw itself. Figure 19 shows a class diagram of the TH
EC
ON
design so far. T
EIN
57 NTA
BS
33 L

Square
uses MA
Client X7
1/2
A1
10
draw() -25
0V
~

Figure 19 Initial design Figure 18 Adapter

The customer likes this so much that they ask you to add a facility for
rounded squares as well as ordinary ones. A rounded square is just a square
whose corners have been rounded (Figure 20).
Drawing a rounded square is a little more complex than drawing a square but
luckily another developer has already written a RoundedSquare class for a
different project, so you decide to reuse it.
But annoyingly the RoundedSquare class has a different interface (Figure 21).
Where Square has the operation draw() the RoundedSquare class has
render(). Of course you could modify the client so it used both operations, Figure 20 Rounded square
but you’d prefer not to have to do that.

31
Unit 9 Architecture, patterns and reuse

The solution is to introduce an Adapter class, which extends Square but has a
RoundedSquare
RoundedSquare attribute, shown as rs : RoundedSquare in Figure 22.
render()

Square
Figure 21 RoundedSquare class uses
Client
draw()

Adapter rs : RoundedSquare
rs : RoundedSquare
draw() render()

Figure 22 Adapter

When the Adapter receives a draw() message it simply forwards a render()


message to the RoundedSquare and the latter produces the required shape
(see Figure 23).

: Client adapter : Adapter rs : RoundedSquare

draw()
render()

Figure 23 Forwarded message

Note that:
. the pattern uses inheritance – an Adapter is a Square
Composition is when one object . the pattern uses composition (here simply represented by an association) –
is included in the state of an Adapter has a RoundedSquare as an attribute.
another.
These two mechanisms are fundamental to design patterns and many patterns
exploit both, as the Adapter pattern does.
Also note that:
. the Adapter is transparent to the other classes – neither the Client nor
RoundedSquare is aware of its existence.
There are many variations on the Adapter pattern. For example, arguments
and return values will usually be involved and the adapter may need to
convert to and from different data formats.

32
5 Reusing design

5.2 Interfaces
The Adapter pattern is concerned with converting between interfaces but the
class model in Figure 22 doesn’t really reflect that. The client still thinks it’s
dealing with a square but any coupling with a specific class is something it
would be desirable to get away from.
It would be far better to pull out the operations the client is expecting to use
and show them as an interface. For this we need the UML notation for an
interface. We shall use this notation frequently later in this unit, and in the
next.
The notation is shown in Figure 24. An interface is indicated by using the
UML stereotype «interface». An interface is not a class and doesn’t define
any implementation for its operations, only their signatures – in other words
the operation name, the types of any arguments and the type of the return
value if there is one.

stereotype «interface»
InterfaceName
empty attribute list
+ operation1
+ operation2

operations and their signature

Figure 24 UML notation for an interface

The actual implementation of the operations is defined within classes that


realise the interface. A class realises an interface by defining operations In Java a class that realises an
corresponding to those of the interface, or by inheriting them from another interface uses the implements
class. keyword.
You met the UML notation + and
A class can realise multiple interfaces, in which case the class has to declare – for public and private visibility
all the operations found in all the interfaces, although it may also have respectively in Block 2 Unit 7.
additional operations.
The UML notation for realisation of an interface is shown in Figure 25. The
relationship is indicated by an arrow similar to the one used to show a
generalisation relationship, but distinguished by the stem of the arrow being a
dashed line.

33
Unit 9 Architecture, patterns and reuse

«interface»
InterfaceName

+ operation1
+ operation2

realisation

ClassName

+ operation1
+ operation2

Figure 25 Realisation of interface

SAQ 5

What is the difference between generalisation and realisation?


Answer

Generalisation expresses a subtyping relationship between two classes. The


subclass is a specialised subtype of its superclass and inherits the attributes
and operations defined by the superclass.
Realisation is not a subtyping relationship but instead expresses the fact
that a class provides an implementation for all the operations specified by
an interface.

5.3 Codifying patterns


A design pattern describes the solution to some frequently recurring design
problem. It documents what experts do when they encounter the problem.
Starting from the mid-1990s a patterns movement emerged in the software
community and many catalogues of patterns have been published. Some have
focused on design while others have addressed different aspects of software
development, from problem identification to writing code.
Pattern catalogues come in many flavours and apply within different activities
of a development process. For instance, Jackson’s Problem Frames (2001) is
a catalogue of basic software problems; Fowler’s Analysis Patterns (1997) is
a catalogue of patterns that are usefully applied during analysis; Grand’s
Patterns in Java (1998, 1999) are idioms, that is patterns that apply to a
specific programming language – Java has one set of idioms, Python a
different set and so on.
The most famous catalogue of design patterns is Design Patterns: Elements
of Reusable Object-Oriented Software by the so-called ‘Gang of Four’
(Gamma et al., 1995). This book was ground-breaking in many ways. It drew

34
5 Reusing design

explicit inspiration from the work of the architect Christopher Alexander and
his colleagues, who described a ‘pattern language’ for physical buildings – a
set of solutions to classic problems.
The book describes 23 patterns, in three categories:
. creational patterns, which deal with ways of creating objects
independently of the clients that will use them
. structural patterns, which deal with relationships among classes and
objects
. behavioural patterns, which deal with how objects communicate and
interact.
Adapter, discussed above, is an example of a structural pattern. In the
following sections we will explore several other patterns, most from the Gang
of Four catalogue, but also the one known as Model-view-controller (MVC).
This was not included by the Gang of Four because, although widespread and
common, it can also be regarded as an architectural pattern and therefore not
one of the purely software patterns the book is concerned with.
We only have space to look at a small selection of patterns, so as well as
Adapter – introduced above – and MVC, we shall concentrate on three
others, all very common:
. Observer (touched on in the discussion of architectural styles), which is a
behavioural pattern
. Factory, which is a creational pattern
. Singleton, another creational pattern often associated with Factory.
The subset of patterns we’ve chosen includes an example of each of the three
categories and is intended to give the flavour of software design patterns
generally. Further patterns may be explored in the module assessment. As
with architectural styles, there are relationships between software patterns,
and applications typically involve several patterns working together.
Design patterns are worth studying not only so you can apply them to
projects you are involved with but also because many APIs and application
programs make extensive use of patterns in their design and you need be able
to recognise them, otherwise you can’t understand the software properly.
The Gang of Four book also introduced a template for documenting design
patterns. The template is quite long, so we will use this shortened version:
. Name
. Purpose (also called intent)
. What problem does it aim to solve?
. How it works – a description of the solution
. When to use it
. Example of use.

35
Unit 9 Architecture, patterns and reuse

Here is the Adapter pattern described in this format.

Adapter
. Name. Adapter.
. Purpose. Allows a client to use a class that has a different interface from
the one the client is expecting.
. How it works. An Adapter class is introduced that provides the client with
the interface it is expecting but forwards client requests to an object of
the class with the incompatible interface (the Adaptee). The interface the
client is expecting is called the Target. Figure 26 shows the structure. The
UML note explains that the Adapter implements operation1() by
forwarding the message operation2() to an instance of the class being
adapted, the Adaptee.

uses «interface»
Client Target
operation1()

Adapter Adaptee
adaptee : Adapter
operation1() operation2()

adaptee.operation2()

Figure 26 Adapting an interface

. When to use it. When you want to use a class with a client that is
expecting a different interface from the one the class provides.
. Example. Legacy software may need to be integrated with a newer system
that uses a different interface.

5.4 Model-view-controller pattern


The next pattern we look at can be regarded as an architectural style as well
as a design pattern. The Model-view-controller (MVC) pattern can represent
the overall architecture of some applications, but it can also be a pattern
applied to designing just part of a system.
MVC was one of the earliest patterns to be described and was first introduced
in the late 1970s in Smalltalk. Its classic use is in the design of applications
that have graphical user interfaces.

36
5 Reusing design

In its most basic form such an application has to do two things:


. interact with the user
. carry out some processing.
It is highly desirable to keep these separate because then you have greater
flexibility. You can choose to have a different kind of user interface without
having to change the part of the program that does the processing. For
example, most user interfaces are graphical but a textual interface with a
screen reader is more likely to suit users with impaired vision. For mobile
devices you will want to use an interface based on a touch screen, and so on.
If the code for the user interface and the code that does the program are all
part of the same module they will be inextricably bound together. Changing
the user interface is likely to require reworking large chunks of other code as
well.
The solution is to have a clean separation. Put all the code for the user
interface and nothing else in one component. Put all the code that does the
processing in another component and keep that component free of interface
code.
As well as this separation of concerns, make sure that each component knows
just enough about the other component to be able to do its job, but no more –
in other words keep coupling as loose as possible.
As a real-life analogue for this, craft suppliers sell clock movements (see
Figure 27).
These are the working parts of a clock but without any clock face or hands.
Clock builders can then add their own clock dial, so a whole range of
different clocks can be constructed that all use an identical design for the
clockwork (see Figure 28).

11 12 1 11 12 1
10 2 10 2 Figure 27 Clock movement
9 3 9 3
8
7 6 5
4 8 4
7 6 5

Figure 28 Different clock faces

In the design pattern the component that does the processing – corresponding
to the clockwork (the interlocking gearwheels and so on in a mechanical
clock) – is called the model. The part the user sees is called the view. A third
component is the controller, which corresponds to the controls that are used
to set the clock to the correct time. Put together, these three make up the
Model-view-controller pattern. There are various different forms
Here is the description of the pattern using our template. of MVC but they all follow a
similar structure.

37
Unit 9 Architecture, patterns and reuse

Model-view-controller pattern
. Name. Model-view-controller (MVC).
. Purpose. Splits user interface interaction into three distinct roles: the
model of the domain, the view representing that domain, and the
controller of changes to the domain.
. How it works. It identifies three roles.
◦ The first is the model, corresponding to an object with some
information about the domain. The model contains data and
behaviour and is not directly accessible to the user. If we consider
MVC as a layered architecture the model resides in the application
domain (or business) layer.
◦ The view is the representation of the model in the user interface: it
displays the output from the system and is what the user perceives
of the model’s state. Both the viewer and the controller reside in
the presentation layer, the layer responsible for user dialogue
aspects.
◦ The controller handles all user inputs that affect the model. The
controller also resides in the presentation layer.
◦ User inputs to the controller cause changes to the model’s state,
which in turn are reflected in the view (see Figure 29).
◦ Although the view and the controller are distinct roles, it is
important to understand that they are not always represented by
different objects. For example, a tick box shows the status of some
setting, making it part of the view, but it also lets the user change
the setting, making it also part of the controller.
◦ This tight integration is typical of many frameworks used for
building user interfaces. In Java Swing, for example, visual
components are typically used for both input and output.

User input

Presentation
View Controller layer

update change
Model Business
layer

Figure 29 Model-view-controller pattern

. When to use it. When you have a user interface that you want kept
separate from the model. The advantages of this include the following.

38
5 Reusing design

◦ Separation of concerns. When designing a model you focus on


business objects and processes. When designing an interface you
are concerned with user interaction.
◦ Facilitating testing. User interfaces are notoriously hard to test
because users can perform so many different sequences of actions,
and such testing is usually done manually. Code implementing
business logic is easier to test and tests can be automated. Keeping
the model separate means that you can take advantage of the
relative ease of testing.
◦ Flexibility. Multiple interfaces may be developed for the same
model, allowing the design to cater for different types of users and
different contexts of use.

. Example. Figure 30 shows an example of a chart (the view) which is a


graphical representation of the state of an underlying model. If the user
changes a value in the table (the controller) the state of the model changes
and the change is propagated to the chart.

controller view
consumption of
hot drinks consumption of hot drinks
60
tea 25 50
coffee 48
chocolate 11 40
30
20 48
10 25
11
0
tea coffee chocolate

model

Figure 30 MVC structure for data display

SAQ 6

Discuss possible advantages of separating the user interface from the


domain logic.
Answer

These are the main advantages.

39
Unit 9 Architecture, patterns and reuse

. The user interface is not affected by changes in the implementation


of the business logic.
. The same domain logic can be used with different user interfaces.
. The business logic can be tested separately from the interface logic.

5.5 Observer pattern


Many online forums allow you to subscribe to them and receive an email
notification whenever a new forum post is made.
This is an example of the behavioural design pattern called Observer. An
observer registers to receive notifications whenever the state of an observable
(usually called the subject) changes (see Figure 31). You should be able to
see that this pattern is closely related to the notification architectural style.

Register me
subject observer

Later...

subject observer
I’ve changed

Figure 31 Subject and observer

As well as being important in its own right the Observer pattern plays an
essential part in the MVC pattern. We said earlier that when user inputs to
the controller lead to changes in the state of the model the changes are
reflected in the view, but we didn’t say how this happens.
The explanation is that the view is an observer that is registered with the
model. Whenever the model changes the view is notified and can then update
itself.

Observer pattern
. Name. Observer (also sometimes known as publish–subscribe like the
notification architectural style which it resembles).
. Purpose. When partitioning a system into individual classes you want the
coupling between them to be loose so you have the flexibility to vary
them independently. But a mechanism is needed to ensure that when the
state of an object changes related objects are updated to keep them in
step.
. How it works.
◦ One object has the role of the subject (or publisher) and one or
more other objects the role of observers (or subscribers). The

40
5 Reusing design

observers register themselves with the subject and if the state of


the subject changes the observers are notified and can then update
themselves.
◦ There are two extreme variants of this pattern. In the push model
the subject sends the observers detailed information about the
change that has occurred. In the pull model the subject simply
notifies the observers that there has been a change and it is the
responsibility of the observers to find out the details they need to
update themselves.
◦ The structure of the pattern is shown in Figure 32. The Subject and
the Observer are interfaces that are implemented by the
corresponding concrete classes.
◦ When the state of the subject changes the observer is sent an
update() message. The implementation of the update() operation in
the ConcreteObserver is then responsible for taking appropriate
action to update the Observer.

Subject
attach(Observer)
detach(Observer) Observer
1 *
notify() update()

invoke update()
on each observer ConcreteSubject
state : State ConcreteObserver
getState() : State
setState(State) update()

do appropriate
action

Figure 32 Observer pattern

. When to use it. When different parts of a system have to be kept in step
with one another without being too tightly coupled.
. Example. As noted, the relationship between the view and the model in an
MVC design can be realised by applying the observer pattern. The view
registers with the model and is notified every time the model’s state
changes, allowing it to update itself to reflect the change.

Exercise 3
In this exercise we consider the design of a new car park. The car park will
have two barriers, one at the entry and one at the exit. The clients for whom
the car park is being designed want a software system for monitoring and
displaying how many free spaces are available at any given moment.

41
Unit 9 Architecture, patterns and reuse

The software will have components corresponding to the entry, the exit, a
monitor that keeps track of the free spaces, and the display. When a car
enters or leaves a signal is sent to the monitor. The display has to be kept in
step with the number of free places.
(a) Briefly discuss the proposed system in terms of the MVC pattern.
(b) What examples of the Observer pattern can you identify in this
application?
(c) Draw a sequence diagram for the interaction that starts with a car
entering the car park.
(d) Suppose a software development company specialising in systems for car
parks decides to set up a product line. Outline very briefly what steps
would be involved.
Solution
(a) The model is the monitor that keeps track of the free spaces. The view is
the display. The controller is the software at the entry and exit that sends
signals to the monitor.
(b) There are two examples of the Observer pattern. The software at the
entry and exit sends notifications to the monitor, which updates its count
of free spaces. When the count changes the display must be notified and
will then need to ask the monitor for the new count so that it can update
itself.
(c) See Figure 33.

: EntryBarrier : Monitor : Display

update()
updateCount()

update()

getState()
updateCount()

Figure 33 Sequence diagram for car park interaction


(d) From the question a product line has already been identified. The next
steps are:
◦ examine existing systems – talk to developers and domain experts
and analyse what the general user will need, generate a domain
model

42
5 Reusing design

◦ from the domain model produce one or more reference


architectures
◦ generate and document a repository of components that can be
used with the reference architectures.

5.6 Singleton and Factory patterns


We have looked at an example of a structural design pattern (Adapter) and at
a behavioural pattern (Observer). Next we look at two creational patterns,
Singleton and Factory.

Singleton pattern
The Singleton pattern is used where there should only be one instance of a
class. An example would be the strategy of implementing use cases by
having all messages from the user interface sent to a single object that is an
instance of a central class. Having more than one instance of this class at
once might cause problems, for example they might interfere with one
another. It’s similar to the idea that a company should only have one chief
executive.
The pattern is described as follows.

Singleton pattern
. Name. Singleton.
. Purpose. In many cases only a single instance of a class is required and
allowing creation of more than one instance would compromise the design
of the system.
. How it works.
◦ The Singleton class provides no public operation for creating
instances. Instead it defines a public operation getInstance() that
lets clients access the unique instance of the class.
◦ One way to implement this is shown in Figure 34. The «singleton»
stereotype denotes a Singleton class. Singleton is responsible for
creating its own unique instance and no other class should be able
to create an instance, so the create() operation is private.

«singleton»
Singleton
- uniqueinstance : Singleton Returns the only instance of
the Singleton class. This
- create() : Singleton unique instance is created the
+ getInstance() : Singleton first time the operation is
invoked.

Figure 34 Singleton pattern

43
Unit 9 Architecture, patterns and reuse

. When to use it. When there must be only one instance of a class. Often
this is associated with some global resource that other classes need access
to.
. Example. Figure 35 illustrates the application of the singleton pattern to
the design of a media manager in a multimedia application. There should
be only one instance of the manager, which is created by the
MediaManager class itself the first time a client accesses the manager.
This strategy of creation on demand is called lazy instantiation.

«singleton»
MediaManager
Returns the only instance of
- manager : MediaManager the MediaManager class.
+ getManager() : MediaManager This unique instance is
- create() : MediaManager created the first time the
operation is invoked.

Figure 35 Media manager example of the Singleton pattern

. Notice only the operation getManager() is public. The attribute manager


and the operation create() are declared as private, so other classes have no
direct access to them
SAQ 7

Where in the hotel system might we use a singleton?


Answer

We could use a singleton instance of HotelChain as a system object that all


messages from the user interface are sent to.

Factory pattern
The second creational pattern is Factory. A factory is a specialised object for
creating and initialising objects needed by clients. The name is taken from
real-life factories, which are facilities dedicated to manufacturing products
required by their clients. In the design pattern the object the factory produces
is called the product.
You may ask why such a factory object is needed. Why can’t clients create
other objects directly, or assign responsibly for creating them to an
appropriate class?
If significant effort is involved in initialising an object, making domain
classes responsible can be a poor design, because:
. initialisation of the required object may be complex and depend on
information the domain classes don’t know
. details may be subject to frequent change and can depend on the
execution environment

44
5 Reusing design

. clients in different parts of the system may need the same product and
there is a risk the creation and initialisation code will be duplicated
. the level of abstraction may be wrong. Classes that are modelling the
system at a relatively high level may be cluttered up with a lot of low-
level implementational details.
We shall look at two forms of the Factory pattern, beginning with the simple
factory.
The pattern is described as follows.

Factory pattern
. Name. Factory.
. Purpose. If the creation and initialisation of an object is complex and
liable to change, making clients responsible for the task introduces an
undesirable level of coupling. Encapsulating the creation in a dedicated
factory class hides the details from the client and reduces the coupling.
. How it works.
◦ The structure is shown in Figure 36. The client has a dependency,
shown by a dashed line, on the factory for the creation of the
product, and the factory depends on the Product class to create one
of its instances. The UML stereotype «create» indicates that the
dependency between Factory and Product is at object creation.
This expresses the fact that the factory must know what class to
instantiate.
◦ The factory is often a singleton, as in Figure 36. Having a single
instance and making it available through a system-wide access
point makes it possible for all clients that require the product to
use the same factory and avoid duplicated code.

request creation
Client

uses

Product
«singleton»
create Factory
+ method1()
+ method2()
Creates an instance
+ createProduct(): Product
of class Product

Figure 36 Factory pattern

. When to use it. Whenever object creation and initialisation is complex or


depends on information that clients may not know or is likely to change.

45
Unit 9 Architecture, patterns and reuse

. Example. Many applications need to use a database management system


(DBMS) for storage. A DBMS is an independent program that
applications communicate with using a particular protocol. A suitable
object can handle the communications but needs to be created and
configured correctly for the DBMS concerned. A connection factory can
provide the required object without the client application needing to know
any details.
SAQ 8

Suggest some quality requirements that the Factory pattern might help
satisfy.
Answer

The ones we thought of were maintainability and portability (and flexibility


if it is counted as distinct from maintainability). You may have come up
with others.

Exercise 4
Suppose a messaging system requires clients to acquire a connection before
they can use its facilities. The interaction in Figure 37 shows how a client
can get a connection without needing to know the details of how this object
is actually created. The client first requests a context object it already knows
about to supply a factory object and then delegates the creation of the
connection to the factory.

1: lookup(factoryName)
: Client context : Context
1
2: createConnection()

{new}
connection : Connection
{new} 2.1: create() factory : ConnectionFactory

Figure 37 Interaction for getting new connection

Based on this collaboration:


(a) draw a class diagram for the classes involved, similar to the UML
structure shown in Figure 36 but with the addition of an additional class
Context (assume both the Context and the ConnectionFactory are
singletons)
(b) give the contract (i.e. the specification) of the operation in the factory
class that realises the creation of a Connection object
(c) redraw the interaction of Figure 37 as a sequence diagram.
Solution

46
5 Reusing design

(a) The class diagram is shown in Figure 38.

«singleton»
Context
Client
+ lookup() : ConnectionFactory

requests creation
uses

«singleton»
ConnectionFactory
connection : Connection «create»
{new}
+ createConnection() : Connection

Figure 38 Class diagram for getting new connection

(b) context ConnectionFactory::createConnection() :


Connection
pre:
- - none
post:
- - a new Connection object will have been created and
returned

(c) The sequence diagram is shown in Figure 39.

: Client context : Context factory : ConnectionFactory

lookup(factoryName)

createConnection()
create()
connection : Connection

Figure 39 Interaction as a sequence diagram

The Factory Method pattern develops the idea of a factory further and
reduces coupling to the minimum possible. A client can use the factory and
then the product via interfaces, without knowing the actual classes of the
objects involved.

47
Unit 9 Architecture, patterns and reuse

The pattern is described as follows.

Factory method pattern


. Name. Factory method.
. Purpose. Sometimes a client requires a factory for a product without
knowing the actual class of the product, only its interface.
. How it works.
◦ The structure, which is quite complex, is shown in Figure 40.

requests creation
Client

uses

«interface» «interface»
ProductIF FactoryIF

+ method1() + createProduct() : ProductIF


+ method2()

Product
«singleton»
«create» Factory
creates an instance of
+ method1() class Product, a
+ method2() realisation of ProductIF
+ createProduct() : ProductIF

Figure 40 Factory method pattern

◦ The client is not aware of the actual class of the factory or the
product, and interacts with them only via the interfaces. At run
time the appropriate class for the factory will be decided in some
way, for example from a configuration file or from a system
setting, and the concrete factory instantiated. The factory will then
create a product of the required class, without the client needing to
know what that class is.
. When to use it. When the decision about what concrete product to create
needs to be deferred.
. Example. Consider a framework for document generation, in which
applications can define application-specific documents that can be
manipulated by an editor. The application of the Factory Method is shown
in Figure 41.

48
5 Reusing design

requests creation
Editor

edits

«interface»
«interface»
DocumentIF
DocumentFactoryIF

+ open()
+ createDocument() : DocumentIF
+ close()
+ save()

Document
«singleton»
«create» DocumentFactory
+ open()
+ close()
+ save() + createDocument() : DocumentIF

Figure 41 Document factory

SAQ 9

Given the document factory example in Figure 41, how would you modify
the class diagram to include a factory for a new type of document? None
of the interfaces should be modified.
Answer

A possible solution is given in Figure 42. A new class representing the


factory is included as well as a class representing the new document.
Creation requests to the new factory, AnotherDocumentFactory, will result
in creation of instances of AnotherDocument.

49
Unit 9 Architecture, patterns and reuse

requests creation
Editor

edits

«interface» «interface»
DocumentIF DocumentFactoryIF

+ open() + createDocument() : DocumentIF


+ close()
+ save()

Document AnotherDocument
«singleton» «singleton»
DocumentFactory AnotherDocumentFactory
+ open() + open()
+ close() + close()
+ save() + save() + createDocument() : DocumentIF + createDocument() : DocumentIF

Figure 42 Document factory with new document type

5.7 Summary of section


In this section you were introduced to design patterns, which are reusable
solutions to problems at a more detailed level. You first met the Adapter
pattern, which addresses the common problem of an object having a different
interface from the one expected by another object.
Many catalogues of different patterns have been produced, dealing with
different aspects of software development. The most famous example is the
catalogue of design pattern published by the Gang of Four. This catalogued
23 design patterns in three categories: creational, structural and behavioural.
You learned about a simplified form of the template the Gang of Four
introduced for describing design patterns and saw it applied to Adapter,
which is a structural pattern.
Other very common design patterns include the Model-view-controller
(MVC) pattern, the Observer pattern, which is behavioural, and the Singleton
and Factory patterns, which are creational. You also learned of some
relationships between different design patterns: for instance in the Factory
pattern the factory is often a singleton.

50
6 Summary

Summary 6
In this unit you have seen where architecture fits into the development
process, what factors drive architectural decisions and what parts of a design
are capable of being reused across a range of systems.
We defined software architecture and examined some real-life examples. Both
requirements and architecture need to be considered in parallel from the
beginning of any project. Although it might seem there is some contradiction
between architecture and agile practices it turns out the two are mutually
supportive.
We looked at how non-functional rather than functional requirements drive
architectural decisions and introduced the concept of architecturally
significant requirements (ASRs). Architectural views are representations of a
system’s architecture from the outlook of particular groups of stakeholders
and show how the system will address their concerns.
We next explored forms of reuse. Architectural styles represent basic
structural forms an architecture can take. Styles can be combined to make
bigger architectures and most systems involve a mixture of styles.
Frameworks consist of a reusable architecture and a set of components that
can be slotted into it. A domain-specific framework incorporating specialist
knowledge about a family of closely related systems is called a product line.
Finally you learned that software design patterns are a way of documenting
solutions to frequently occurring problems at the level of classes and
interaction between objects. You were introduced to several common design
patterns, including examples of each of the creational, structural and
behavioural categories.
On completion of this unit you should be able to:
. give an example of a software architecture
. explain that requirements and architecture evolve in parallel
. explain that non-functional requirements and other architecturally
significant requirements, not functional requirements, are what drive
architectural decisions
. understand the need for different architectural views and how these relate
to the concerns of different groups of stakeholders
. recognise common architectural styles and understand that most systems
will contain examples of several styles
. understand the concept of a framework and a product line
. appreciate the concept of a software design pattern, talk about design
patterns, and recognise and understand the design patterns studied in the
unit.

51
Unit 9 Architecture, patterns and reuse

References
Alexander, C. (1964) Notes on the Synthesis of Form, Cambridge, MA, Harvard
University Press.
Bass, L., Clements, P. and Kazman, R. (2003) Software Architecture in Practice (2nd
edn), Boston, Addison Wesley.
Booch, G. (2000) 'The future of software', Proceedings of the 22nd international
conference on software engineering. Limerick, June 4–11 2000. New York, ACM,
p. 3.
Chen, L., Barbar, M. A. and Nuseibeh, B. (2012) Characterizing Architecturally
Significant Requirements [Online]. Available at http://malibabar.files.wordpress.com/
2012/11/characterizingarchitecturallysignificant-chenl-ieee-software.pdf (Accessed 7
September 2013).
Falessi, D., Cantone, G., Sarcia’, S.A., Calavaro, G., Subiaco, P. and D’Amore, C.
(2010) ‘Peaceful coexistence: agile developer perspectives on software architecture’,
IEEE Software, vol. 27, no. 2, pp. 23–25.
Fowler, M. (1997) Analysis Patterns, California, Addison Wesley.
Gamma, E., Johnson, R., Vlissides, J. and Helm, R. (1995) Design Patterns:
Elements of Reusable Object-Oriented Software, Wokingham, Addison-Wesley.
Grand, M. (1998) Patterns in Java, Volume 1, Canada, John Wiley & Sons.
Grand, M. (1999) Patterns in Java, Volume 2, Canada, John Wiley & Sons.
ISO/IEC/IEEE (2014) 42010: Systems and Software Engineering – Architecture
Description [Online]. Available at www.iso-architecture.org/42010/index.html
(Accessed 2 October 2014).
Jackson, M. A. (2001) Problem Frames, Harlow, Addison Wesley.
Kruchten, P. (1995) ‘Architectural blueprints: the “4+1” view model of software
architecture’, IEEE Software, vol. 12, no. 6, pp. 42–50 [Online]. Available at www3.
software.ibm.com/ibmdl/pub/software/rational/web/whitepapers/2003/Pbk4p1.pdf
(Accessed 7 September 2013).
Kruchten, P. (2004) The Rational Unified Process, Reading, MA, Addison Wesley.
Nuseibeh, B. (2001) ‘Weaving together requirements and architecture’, Computer,
vol. 34, no. 3, pp. 115–119 [Online]. Available at http://oro.open.ac.uk/2213/1/
00910904.pdf (Accessed 7 September 2013).
Software Product Line Conference (2012) Product line hall of fame: Siemens
Healthcare: software product line for 3D routine and advanced reading [Online].
Available at http://splc.net/fame/siemens.html (Accessed 7 September 2013).
Shaw, M. and Garlan, D. (1996) Software Architecture: Perspectives on an Emerging
Discipline, Upper Saddle River, NJ, Prentice Hall.

52
Unit 10 Building blocks and
enterprise architectures
Contents
1 Introduction 57
2 Components and interfaces 58
2.1 Software components 58
2.2 Interfaces 59
2.3 Objects as components 62
2.4 Summary of section 64
3 Service-oriented architecture 66
3.1 Services 66
3.2 Summary of section 70
4 Architecture, quality attributes and tactics 71
4.1 Quality attribute scenarios 71
4.2 Other quality attributes 75
4.3 Tactics for quality attributes 77
4.4 Tactics for flexibility 81
4.5 Summary of section 83
5 Putting it all together – enterprise architecture 84
5.1 Introducing Java EE 84
5.2 An example EJB 87
5.3 An example service 88
5.4 An architecture for the hotel system 91
5.5 Summary of section 95
6 Summary 97
References 98
1 Introduction

Introduction 1
To create architecture is to put in order. Put what in order? Function and
objects.
Le Corbusier

We begin this unit with component-based development (CBD), which aims


to develop systems by assembling them from reusable self-contained software
components. Components interact only through their interfaces and hide
details of their implementation.
Figure 1 Building designed by
Components have two types of interface: a provided interface, which specifies Le Corbusier (located at
what the component makes available to other components, and a required Chandigarh, Punjab, India)
interface, which specifies what the component expects from other
components. The interface of a component is more than just a set of
operations: it includes other information about the conditions under which the
component can be used.
We then look briefly at using objects as components and consider issues such
as the contract between two interacting components and how this influences
whether one component can be replaced by another.
Components usually rely on a particular technology, and the architect is
normally aware where they are deployed. It is possible to take the process of
abstraction (hiding implementation) a step further and view the unit of reuse
as the functionality provided, independently of the technology or the location
at which the service is actually running. We discuss service-oriented
architecture (SOA) software, which is structured simply as a set of services.
You have already seen that the architecture of a system must not just deliver
the functionality required, but also meet quality attribute requirements, since
otherwise the delivered system will fail to address the concerns of the various
stakeholder groups. In this unit we introduce quality attribute scenarios, a
way of documenting what particular quality attributes will mean in practice,
in a way that will help us make architectural decisions.
This raises the question of what principles software architects can follow to
ensure that various types of quality requirement are met. For each type of
attribute we can describe a set of tactics: reusable solutions to the problem of
achieving particular qualities.
Finally we look at enterprise architectures: software systems such as those
used by large organisations. We briefly introduce Java Platform, Enterprise
Edition (Java EE) which is a framework for constructing enterprise systems
and give an example of how a component and a service can each be
implemented in Java EE. We end by presenting one possible enterprise
architecture for the hotel system.

57
Unit 10 Building blocks and enterprise architectures

2 Components and interfaces


2.1 Software components
In earlier units we have used the term component to mean any more or less
self-contained software unit. In this section a component has a more specific
meaning, as a unit of software structured according to the following
principles:
. A component encapsulates data and operations.
. A component is fully documented and thoroughly tested.
. A component’s specification clearly separates the component’s interface
from its implementation.
. A component’s specification may include non-functional characteristics.
. A component is known to client components only through its interface.

SAQ 1

Think of another concept that uses principles similar to those above.


Answer

An object in an object-oriented From the above principles, a component closely resembles the concept of
language is often how a an object in an object-oriented language.
component is implemented.
What motivates the study of such components is the possibility of
constructing software applications by flexibly plugging together pre-existing
components. This vision is inspired by the analogy with electronic
engineering, where standard off-the-shelf components can be plugged into
one another to create entire complex systems. If a similar practice can be
followed in the construction of software it offers many advantages.

SAQ 2

Think of some possible advantages and disadvantages of building software


by plugging together off-the-shelf components.
Answer

We thought of the following:


Advantages
. Reusing a standard component should be cheaper than developing
software from scratch.
. Using off-the-shelf components will allow applications to be
developed more quickly.
. Standard components will have been used in many other projects, so
their behaviour will be well understood and any bugs are likely to be
known.

58
2 Components and interfaces

. Components are pluggable, so it is often possible to replace one


component with another providing it has the same interface and
behaves in the same way.
Disadvantages
. Using standard components may restrict what we can do since we
have to work with the capability of the components.
. Some additional software is likely to be needed as ‘plumbing’.
. If the interfaces of components are incompatible, adapters will have
to be written.
. Creating a system by plugging together off-the-shelf components
may not be as simple as it sounds.

This approach is known as component-based development (CBD). It is


particularly important in the development of enterprise applications where
being able to assemble or modify software quickly by plugging together
components helps meet the rapidly changing requirements many organisations
experience.

2.2 Interfaces
An application built using components is likely to be structured using a call-
return architectural style, in which components interact by making method
invocations on one another. Two interacting components do so via a common
interface. A component may have several different interfaces, representing
different points of interconnection.
Interfaces come in two kinds, because in every interaction between two
components one component is requiring an operation and the other
component is providing it. A description of a set of operations a component
makes available to other components is a provided interface.
Similarly a description of a set of operations a component needs from other
components is a required interface. The required interface is also
known as the component’s
When two components interact it is through their interfaces, with a required context dependencies.
interface of one matching a provided interface of another. Once we decide to
use a particular component we must also include a component or components
that satisfy its required interface, so we are not simply describing the
component itself but also specifying other components that are needed.
In UML a component is represented as a stereotyped box, where the
stereotype can be either graphical or textual. Figure 2 illustrates a component
in the two notations. The figure also shows the notation for required and
provided interfaces, graphical in the first notation and using the stereotypes
«provided» and «required» in the second.
Two interconnected components are shown by plugging the required interface
of one into the provided interface of another, using a ball for the provided
interface and a cup for the required one. This is illustrated in Figure 3, where
a component Account makes use of some security services provided by a

59
Unit 10 Building blocks and enterprise architectures

provided interface graphical stereotype textual stereotype

IProvided «component»
MyComponent
MyComponent «provided»
IProvided
IRequired «required»
IRequired
required interface
(a) (b)

Figure 2 Components in UML

Security component. As you can see, we use the convention that interface
names are prefixed by an upper case ‘I’.

IAccountManagement
IEncryption

Account Security
IAccessControl

Figure 3 Interconnecting components

Representing interfaces in UML


You saw in Unit 9 that an interface is represented by a box marked with the
stereotype «interface». The box is similar to the notation for a class but since
an interface does not have attributes the middle compartment is always empty
(Figure 4).

stereotype «interface»
IInterfaceName
no attributes
Operations
Operations

operation signatures

Figure 4 Interface

Example 1
Figure 5 shows a catalogue component offering a number of services,
including some support for browsing the catalogue and searching for
particular products.

60
2 Components and interfaces

Catalogue
ISearching
ISearching
SearchEngine

IBrowsing
IBrowsing
IndexEngine Catalogue

Figure 5 Catalogue component

Assuming a client can browse alphabetically or by category of product, and


search by entering a keyword, we could specify the provided interfaces of the
component as shown in Figure 6.

«interface» «interface»
IBrowsing ISearching

browseAlphabetically() : Collection search(keyword : String) : Collection


browseByCategory() : Collection

Figure 6 Interface specifications

Interfaces as sets of behavioural assumptions


At first sight the interface of a component might seem to be described fully if
we know the signatures of its operations and something about what they do –
their semantics.
In fact an interface is much more than this. In an early work Parnas (1972,
p. 339) described it as ‘the set of assumptions that components make about
each other’. This might include information about a whole range of issues. It
will include assertions (pre- and postconditions and any invariants that apply
to externally visible properties of the component) that you have learnt about
in earlier units.
But assumptions go wider than this. For example a component might rely on
a database existing, or on another component having processed some data, or
on some resource being available when required. Other assumptions might be
concerned with what should happen if a failure occurs when an operation is
invoked: will some sort of exception be thrown?
Obviously assumptions of this kind need to be included in interface
descriptions, since the successful operation of the system will depend on
them.
We can use the terms assume and guarantee respectively to mean the
conditions the component needs in order to operate correctly, and the

61
Unit 10 Building blocks and enterprise architectures

promises it makes to other components. If the assumptions about the


environment of the component are true then the component will meet the
guarantees. The relationship between a component and its client components
takes the form of a contract. You can think of this as similar to a contract
based on pre- and postconditions but extending to a wider range of
considerations.
This way of reasoning is important because components are by definition
self-contained and potentially replaceable elements that will be used in many
different applications, and we need a way of specifying and testing them
independently of any particular application.

Too much information?


In general an interface description should not reveal anything about the
implementation of a component, only its externally visible properties. If
details of the implementation are exposed they may become part of the
assumptions of other components. Then it will be difficult or impossible to
change the implementation later if we wish, and the component will no
longer be replaceable.

2.3 Objects as components


You saw earlier that an object in an object-oriented language follows similar
Components can also be principles to a component and in fact components are often implemented as
implemented using non-object- objects.
oriented technologies but will
still communicate through SAQ 3
provided and required interfaces
in the same way. Suppose a component is implemented as an instance of a Java class. What
corresponds to:
. the provided interface of the component
. the required interface of the component?
Answer

The provided interface consists of all the public methods in the class.
The required interface consists of all the methods from other classes that
the component’s methods make use of.
In object-oriented languages an object belonging to a subclass is allowed
to replace an object of the parent class. This is substitutability, which was
discussed in Block 2 Unit 5.

SAQ 4

In the context of software components, describe a concept that is similar to


substitutability.

62
2 Components and interfaces

Answer

Components are replaceable: a component can be replaced by another that


does the same job.

Exercise 1
What would the assume–guarantee contract for a component include and how
do the assume and guarantee relate to the component’s provided and required
interfaces?
Solution
The contract for a component would be:

1 the pre- and postcondition of all the operations in the provided interface
of the component
2 the invariants that apply to any publicly visible properties of the
component
3 the required interface of the component and all the assumptions of the
required interface
4 any other assumptions about the environment in which the component will
operate.
The assume would be the preconditions of the operations, and items 3 and 4
of the contract above.
The guarantee would be the postcondition of the operations and item 2 of the
contract above.

Exercise 2
Suppose component X is replaced by component Y, which has different
assumptions and guarantees from X. Drawing on your knowledge of design
by contract (DbC) suggest what restrictions must apply to the assume and
guarantee if Y is to be an acceptable replacement for X. Illustrate your
answer with an example.
Solution
The assumptions made by Y must be the same as those made by X, or
weaker. The guarantee Y makes must be the same as the one X makes, or
stronger. In other words Y must not demand more, or deliver less, than X.
For example X might accept up to a million items and promise to process
them with 99 per cent accuracy. If Y restricts the maximum to half a million
items it is demanding more. If it promises only 90 per cent accuracy it is
delivering less.

Interacting components
When two components interact, one is a client for an operation supplied by
the other. The operation is part of the client's required interface and the

63
Unit 10 Building blocks and enterprise architectures

supplier's provided interface. Often the interaction will follow a call-return


style, although other types of communication are possible.
If we are using DbC, then before calling the operation the client must ensure
that the preconditions of the operation are met and that the supplier's other
assumptions are also satisfied.
This doesn't necessarily mean that the client has to check every aspect of the
supplier's assumptions. Many will be true automatically as long as the whole
system has been initialised properly. For example, if the supplier depends on
a database system having started, this would normally have occurred during
system start-up.
However, a client will usually need to check the precondition of any
operation it calls. The fragment of client code below shows a typical idiom
for checking before invoking an operation.
if (pre-condition) {
// invoke operation on supplier
}
else {
// take alternative action
}

If we are not following strict DbC, then theoretically a client does not have
to ensure the precondition. However, if the precondition is not satisfied then
it is unlikely that something good will happen as a result of invoking the
operation; it is therefore generally in the client's interest to do a check
following the pattern above, even where we are not using strict DbC.

Integration and adapters


Sometimes a component-based system needs to incorporate other software
that doesn't follow the component model. For example we might want to
reuse items of legacy software rather than go to the expense of replacing
them.
An adapter is also often called a In these cases it is generally possible to write an adapter that gives the
wrapper. software a provided and required interface that makes it look externally like
an object-oriented component, even though it is implemented in a different
way ‘under the bonnet’. The possibility of writing adapters increases the
potential for software reuse.

2.4 Summary of section


In this section you were first introduced to component-based development,
which structures software by combining reusable self-contained elements that
hide their implementation behind interfaces.
You saw how components and their interfaces can be shown in UML. A
component has a provided interface – the operations it provides to other
components – and a required interface – the services it requires from other
components.

64
2 Components and interfaces

More generally we can regard an interface as everything components assume


about one another. This includes not just the operations concerned, with their
pre- and postconditions, but any other dependencies a component has. You
saw that a component can be described by a set of conditions that it needs in
order to operate correctly and a set of promises it makes to other
components; together they form an assume–guarantee contract.
You then learnt about objects as components and saw how client components
interact with supplier components, through the required interface of the first
and the provided interface of the other. For one component to replace another
the assumptions must be the same or weaker, and the guarantee must be the
same or stronger. The client must normally ensure the assumptions of the
supplier, and this involves checking the preconditions of operations even if
DbC is not being followed strictly.
Finally you learnt that even software not originally written to be a component
can often be integrated into a component-based system by wrapping it with
an adapter that provides appropriate interfaces.

65
Unit 10 Building blocks and enterprise architectures

3 Service-oriented architecture
In the previous section we looked at how an application can be constructed
by combining components. A component has an interface, and other
components make use of the services the component offers by invoking its
operations, typically using a call-return style.
Components keep coupling low by hiding their implementation and
communicating only via their interfaces, but the flexibility achievable with
component-based development is limited by factors such as dependence on a
particular platform or language and the effort of integrating legacy systems.
In Unit 1 you were introduced to services, which offer a different way to
structure applications. In this section we look at service-oriented architecture
(SOA).

3.1 Services
There are many definitions of a service but we shall use the following:

A service is an abstract description of some unit of business functionality,


usually described in terms that are meaningful from both a business and a
technical perspective.

This defines a service as a logical entity. In order to be used, a service must


be implemented by a service provider, and accessed over a network by a
client, also known as a service consumer or a service requester.
You can think of a service as being what a component provides, but the
component itself has been abstracted away, leaving the service as something
the requester simply accesses by connecting to an end-point, without any
knowledge of where or how the service is provided. This is a more loosely
coupled model than component-based development.
By combining a set of services it is possible to build applications, and this
way of structuring software is known as service-oriented architecture
(SOA).

Services versus components


In many ways services resemble components, and indeed components can be
used to implement services. Like components, services:
. have well defined interfaces which specify how other software can interact
with them
. aim to be loosely coupled, by not exposing details of their
implementation, thus allowing it to be changed without disturbing other
parts of the system

66
3 Service-oriented architecture

. can be composed – services can be combined to build up more complex


functionality
. aim to be reusable, so that the same service can be used as part of many
different applications.
However, there are key differences:
. Communication with components tends to depend on proprietary
technologies, which restricts interoperability. Services use standard
communication protocols, which allows them to interoperate in a
platform- and language-independent way.
. Services are discoverable. Clients can access a repository to find details of
available services.
. Components run on computers controlled by the organisation using them
and if many organisations use a component it executes in multiple
locations. A service in contrast resides on a provider server, typically
owned by a different organisation, and executes at a single end-point that
all clients communicate with.
. Services should be autonomous and as far as possible independent of
other services. So, for example, they do not have a ‘requires’ interface.
This is so that they can be more reusable and also more insulated from
changes in their operating environment. Not all dependencies can be
avoided of course, for example a service may rely on a particular
database.
In addition, services have two properties which may sometimes apply to
components as well:
Statelessness. A service or component is stateless if it responds to each
request as a ‘one-off’, without retaining any memory of previous requests.
This reduces complexity, because each request can be dealt with in the same
way. It also allows increased demand to be dealt with by simply deploying
more copies of the service or component, since it will not matter which copy
is allocated to which request.
Location transparency. Clients can use a service without needing to know
its physical location – that is, what computer it is actually running on. It is
also possible for components to be location transparent and be invoked using
a logical name rather than a physical address. The advantage of location
transparency is that the service or component can be moved to a different
computer without clients being affected.

Find, bind and invoke model


An SOA collaboration typically involves a ‘find, bind and invoke’ cycle as
shown in Figure 7.

67
Unit 10 Building blocks and enterprise architectures

Find Service
Consumer Registry description

Bind and invoke Publish

Service
description
Provider
Service

Figure 7 ’Find, bind and invoke’ cycle


The elements in this model are as follows:
. The consumer.
. The service. Each service has a service description that specifies how the
client can interact with it. The service description will define the signature
of the service and may describe other aspects of the contract, although
this is not always the case and some information about the service
typically needs to be provided outside the service description.
. The provider, which is the platform on which the service is implemented.
It accepts and executes requests from clients.
. The registry (or locator) which allows clients to find services.
The service description for each service is published in the registry by the
associated service provider. In a typical collaboration a consumer queries the
registry, to find the details of a particular service or to discover a service that
meets given criteria. The registry provides the consumer with the service
description and information that allows the consumer to bind to the service
and invoke its operations.

Kinds of service
The discussion in this subsection and the following one are based on
Sommerville (2011).
We can distinguish three kinds of service:
. utility services, which provide some generic functionality useful in a wide
range of applications
. business services, which implement a specific business function
. coordination services, which coordinate workflows composed of a number
of individual services.

68
3 Service-oriented architecture

Services can also be classified as either task-oriented or entity-oriented


services. Task-oriented services are related to business activities (business
processes), whereas entity-oriented services are related to business entities
(business objects).
Coordination services are, by definition, task oriented, but the other two kinds
of service can be either task- or entity-oriented. Examples of these
classifications are given in Table 1.

Table 1 Types of service

Utility Business Coordination


Task convert from one check membership process membership
currency to another application form application
Entity currency member

Source: adapted from Somerville, 2011

Composing an application
When we build an application within an SOA we must choose suitable
services and then compose them into a workflow, so that the services are
invoked in the proper sequence. This overall coordination is referred to as
service orchestration, and successful orchestration is obviously essential to
whether or not the application will work.
In highly simplified form, developing an application by composing services
within an SOA consists of the following stages, assuming the requirements
are already known:
. Design a workflow and specify what services will be needed.
. Use the registry to discover candidate services.
. From the candidates select a suitable set of services.
. Orchestrate the chosen services according to the workflow. This may be
done using a special-purpose orchestration language, or we may write an
orchestrating program in a standard language such as Java.
. Test the application and correct any faults found.

Advantages of SOA
Service-oriented architecture offers a number of potential advantages,
including the following:
. Agile and flexible response. SOA supports a flexible business model that
can respond quickly to changes in customers’ requirements, new business
opportunities or competitive threats. Developers can quickly assemble new
applications by combining existing services.
. Less duplication. If several parts of a business require the same function,
it can be packaged as a service and made available for reuse.

69
Unit 10 Building blocks and enterprise architectures

. Integration of legacy applications. Legacy software can be wrapped as a


service and made to interoperate with other applications.
. Use of third-party services. Systems can easily incorporate functions, for
example credit card validation or online payment, provided as services by
external suppliers.
. Language independence. Services written in different languages can
interoperate using standard protocols.

3.2 Summary of section


In this section we looked at services. A service is an abstraction of some unit
of business functionality, and service-oriented architecture structures software
as a set of services.
Services share features with components, for example they have well defined
interfaces and hide details of their implementation to keep coupling low.
However, unlike components, services use standard communication protocols
rather than proprietary technologies, and they are normally discoverable, so
clients can locate them using a registry.
You next learnt about the ‘find, bind and invoke’ cycle. Service providers
publish a description of each service in a registry. Typically the consumer
queries the registry for details of a suitable service, the registry provides the
consumer with the information necessary to use the service, and the consumer
binds to the service and invokes its operations.
You saw that services can be classified into three types: utility services,
business services and coordination services, and further classified as task-
orientated or entity-orientated.
You learnt that, to compose an application, appropriate services must be
selected and then orchestrated into a workflow, and you read an outline of the
steps needed to develop a system using service-oriented architecture.
Finally you saw some of the advantages of service-oriented architecture.

70
4 Architecture, quality attributes and tactics

Architecture, quality attributes and tactics 4


You saw in Unit 9 that quality attributes are the most prominent category of
architecturally significant requirements. In Block 1 Unit 2 you learnt that to
be useful a non-functional requirement must be unambiguous and measurable,
and that fit criteria are a way to tell objectively if a non-functional quality
has been met.
In this section we extend these ideas. We will introduce quality attribute
scenarios, a tool that allows specification of non-functional requirements in a
more precise and detailed form which can be used to help make architectural
decisions. Writing quality attribute scenarios is often quite difficult but it is
an essential step in developing successful systems.
As developers we naturally tend to focus on functional requirements but
functionality alone can never be enough for a satisfactory system. As Chung
and do Prado Leite (2009) say in a survey of non-functional requirements,
‘the functionality is not useful or usable without the necessary non-functional
characteristics’.
Imagine, for example, that we deliver a system that is inadequate from the
point of view of performance, or usability, or security, or other quality
attributes. Even though it meets the functional requirements in every way, for
the stakeholders this product will be a failure. You will learn some tactics to
help you take decisions about architecture that have an impact on quality
attributes.

4.1 Quality attribute scenarios


Much of this section draws on the discussion of quality attribute scenarios by
Bass et al. (2003, 2012). They define a six-part model which is shown in
Figure 8.

environment

artefact
stimulus response
source measure

Figure 8 Quality attribute scenario: six-part model

In this model:
. The source is a human actor, another system, or anything else that can
generate a stimulus.
. The stimulus is any kind of event or request.

71
Unit 10 Building blocks and enterprise architectures

. The artefact is what will respond to the stimulus. It might be a running


component or service if we are considering performance for example, but
it could be code or documentation if we are interested in maintainability.
. The environment specifies the conditions which the scenario assumes the
artefact will be operating under. For instance if we are concerned with
performance the environment might be either ‘normal operation’ or
‘overloaded’.
. The response is what happens as a result of the artefact receiving the
stimulus.
. The response measure is an objective yardstick by which we can test if
the requirement has been met.
This model is powerful and can be used for any quality attribute. Take
portability for example: the source might be a developer, the stimulus a wish
to port the system to a new platform, the artefact the code, the environment a
specification of the deployment time, the response that the system has
successfully migrated, and the response measure that the task is completed in
a specific time.
For a given attribute we can list what types of value can occur for each of
the six parts. Table 2 shows the possibilities, including sample metrics, that
Bass et al. give for performance.

Table 2 Types of value for performance scenarios

Part of scenario Type of value


Source internal
external
Stimulus periodic events
sporadic events
bursty events
stochastic events
Artefact system
Environment normal
overload
Response process stimuli
change level of service
Response measure latency
deadline
throughput
jitter
miss rate
data loss

Source: adapted from Bass et al, 2012

A sporadic event is one that is infrequent and isolated. A stochastic event is


one occurring randomly but according to a well-defined probability
distribution: for example heads coming up when a coin is flipped. Bursty

72
4 Architecture, quality attributes and tactics

events are ones coming in sudden and unpredictable clusters overlaying


normally low background activity.
Latency is the time taken to process a stimulus, for example how long does a
search engine take to respond to a query? Throughput is the number of
events dealt with in a given time. Jitter is the amount of variation in latency:
for example, are some responses very quick while others take much longer?
Miss rate measures the proportion of events that are not responded to, and
data loss measures how much data is lost because the system fails to record
it.
Table 2 lists general values. To produce a particular scenario that expresses a
non-functional requirement we must make an appropriate selection of value
for each part of the scenario, and specify what measure is needed for the
requirement to be met – that is, the fit criterion.

Example 2

The owners of a multi-storey car park require a system to monitor the This example is unconnected
number of free spaces the car park has available at any given moment and with the car park example used
display the information in real time for the benefit of motorists wishing to in Unit 9.
use the car park.
The car park has several entrances and exits. At each, there is a barrier
operated by an independent control unit which has its own computer and runs
its own software. When a car enters or leaves the car park the relevant
control unit sends a signal to a central computer which keeps track of the
free spaces. Whenever the number of free spaces changes, the display is
updated.
On 90 per cent of occasions the display should be updated within 1 second of
a car entering or leaving the car park.
An example scenario is shown in Table 3.

Table 3 Example performance scenario

Part of scenario Type of value Actual value


Source external control unit at entrance
Stimulus stochastic event signal to the central system
Artefact system central system
Environment normal control units, display and communication all
working normally
Response process stimuli adjust free space count, update display
Response measure latency display updated within 1 second or less of
the car entering in 90% of cases

73
Unit 10 Building blocks and enterprise architectures

SAQ 5

Invent a performance scenario similar to the one in Table 3 but for a


search engine.
Answer

We came up with the suggestions in Table 4.

Table 4 Answer to SAQ 5

Part of Type of value Actual value


scenario

Source external web browser, internet user


Stimulus stochastic search request
event
Artefact system search engine
Environment normal browser and internet connection
working normally
Response process return list of search hits
stimuli
Response latency first 10 hits returned within
measure 0.35 seconds for 99% of queries

SAQ 6

Think of situations where the pattern of events for a performance scenario


might be:
. periodic
. sporadic
. bursty.
Don’t spend too long on this; it is only meant to get you thinking.
Answer

Below are examples we thought of, two in each category. Yours will
obviously be different.
Periodic
Anything that occurs regularly, for example information sent at the same
time each day from a weather station, or status information sent every
minute from a spacecraft.
Sporadic
Messages sent to an address for reporting problems, or signals from a
device monitoring earth tremors.
Bursty

74
4 Architecture, quality attributes and tactics

Search queries about a suddenly popular topic, or signals from a device


reporting lightning strikes in a particular area.

SAQ 7

Think of two application areas where the appropriate response measure for
a performance scenario would be something other than latency.
Answer

Here are several examples we thought of:


. a system controlling a plant processing industrial chemicals –
deadline
. an online shopping site – throughput
. an online gaming site – jitter (since users will expect consistent
response times)
. a social networking site – data loss
. an online shopping site – miss rate.

4.2 Other quality attributes


Similar techniques can be applied to any of the quality attributes. We have
space here to explore only one more: usability. We chose this quality because
many aspects of usability are familiar to most people.
Table 5 shows possible types of value for the six parts of a usability scenario,
after Bass et al.

75
Unit 10 Building blocks and enterprise architectures

Table 5 Types of value for usability scenarios

Part of scenario Types of value


Source end users
Stimulus user wants to:
– learn system features
– use system efficiently
– minimise impact of errors
– feel comfortable
Artefact system
Environment setting preferences
using system
Response one or more of:
– supporting learning
– supporting efficient use
– minimising impact of errors
– allowing adaptation
– making user comfortable
Response measure task time
number of errors
number of problems solved
user satisfaction
success rate
time taken or data lost

Source: adapted from Bass et al, 2003

Example 3 provides an example of a specific scenario.

Example 3
A new feature is to be added to the car park system described in Example 2,
which will allow a duty operator to add messages to the display, for example
to advertise if an entry or exit is temporarily closed. The example scenario in
Table 6 is for the case of an experienced operator who wants to use the
system efficiently by reusing text already entered.

Table 6 Example usability scenario

Part of Type of value Actual value


scenario
Source end user experienced operator
Stimulus user wants to: use messaging feature efficiently
– use system
efficiently
Artefact system central system
Environment using system normal system use
Response supporting efficient system allows messages to be reused by
use copy and paste

76
4 Architecture, quality attributes and tactics

Measure task time time saved by copy and paste at least


20 seconds on average

SAQ 8

Invent a usability scenario for a search engine, with the stimulus that the
user wants to minimise the impact of errors.
Answer

We came up with the scenario in Table 7.

Table 7 Answer to SAQ 8

Part of Type of value Actual value


scenario
Source external end user
Stimulus user wants to: minimise impact of typing
– minimise the impact of error in search term
errors

Artefact system search engine


Environment normal browser and internet
connection working
normally
Response minimising impact of suggest corrected spelling
errors for search term
Measure latency spelling suggestion correct
90% of the time

4.3 Tactics for quality attributes


Quality attribute scenarios specify what qualities the system should possess
but not how they can be achieved. For that we turn to design tactics, which
are the various choices available to us when deciding the architecture of a
system.
As an example, if the quality we need to achieve is performance, a possible
tactic is to add more resources to deal with demand. This may not always be
possible for cost reasons, but it is one of the options we should consider.
Tactics are reusable solutions that have emerged from long experience among
software engineers.

SAQ 9

From your study of the module, list some other types of reusable solution,
apart from design tactics, that are available to software engineers.

77
Unit 10 Building blocks and enterprise architectures

Answer

We thought of:
. analysis patterns
. requirements patterns
. architectural styles
. design patterns
. language idioms
. components
. services.

Bass et al. (2003, 2012) have led the way in introducing tactics as a form of
reuse and describing tactics for a representative range of quality attributes,
although tactics can be described for any attribute. Here we have space to
consider only two examples. The ones we chose are performance and
flexibility.
Performance was chosen because performance was one of the quality
attribute scenarios we explored earlier, so it allows us to show an example of
how quality attribute scenarios and the corresponding tactics are related.
Flexibility was selected because it is closely associated with many of the key
concepts of the module. Bass et al. refer to modifiability rather than
flexibility but we shall treat the two terms as meaning the same.
We start by outlining tactics for performance. Our discussion is again adapted
from Bass et al.
Performance, like comedy perhaps, is to do with timing. The system is
presented with a stream of incoming events, which may follow various
pattern and arrival rates. The system should process them to meet quality
requirements measured by latency, deadlines, throughput, and so on –
discussed above in quality attribute scenarios.
Tactics for meeting performance requirements take three main forms.
Tactic 1: Manage demand
How many?
If the frequency of stimuli is controllable, for example if we are periodically
asking a weather station for the current temperature, we can simply sample
the stimuli at a rate the system can keep up with.
Otherwise incoming stimuli are placed in a queue and processed as soon as
possible. The queue will have a maximum length, which in some
circumstances may be exceeded, causing stimuli to be missed, but this is
unavoidable since no system has limitless capacity.
How much?
Processing stimuli requires computations and access to resources – a demand
on the system. Using efficient algorithms and resource requests will reduce
this demand.

78
4 Architecture, quality attributes and tactics

Tactic 2: Manage system capacity


Resource provision
One way to deal with demand is to increase resources: storage, processing or
communications. A trade-off is involved since increasing resources has a
financial cost.
Bottlenecks
There may be bottlenecks: widely used resource such as data with only one
point of access, or computations carried out just in a single location.
If processing stimuli involves such a bottleneck, performance is likely to
suffer. To overcome this we can provide multiple copies of the data, or run
the computation in multiple locations.
Concurrent processing
Wherever possible, stimuli should be processed in parallel. Even where we
do not have multiple processors it is usually possible to process stimuli in
separate interleaved threads of control and so make more effective and
efficient use of resources, for example by ensuring input and output is not
idle, by assigning it to a thread that is currently ready to use it.
Tactic 3: Match resource to demand (also known as arbitrating
resources)
Resources must be matched to demand in a way that best meets quality
requirements.
For example, to minimise latency we would use a scheduling policy that
deals with short tasks before lengthier ones – rather like the ‘10 items or
fewer’ queue in supermarkets.
If deadline is the measure then each task must have a priority and the
scheduler must ensure that higher-priority tasks are dealt with first.
To achieve throughput the scheduling policy must attempt to maximise use of
resources, which means that, whenever a task cannot make progress for some
reason, resources it is using must be temporarily reallocated, allowing other
tasks to use the slack capacity.
Figure 9 summarises performance tactics.

79
Unit 10 Building blocks and enterprise architectures

reduce frequency
of events

manage
limit demand
demand

make processing
more efficient

increase resources

performance manage
reduce bottlenecks
tactics resources

exploit concurrency

arbitrate schedule
resources resources

Figure 9 Performance tactics

80
4 Architecture, quality attributes and tactics

4.4 Tactics for flexibility


SAQ 10

(a) Write down in your own words what flexibility means in relation to
software.
(b) What concepts introduced in the module relate to flexibility? Come
up with as many as you can and write them down. Don’t spend too
long though: 10 minutes or so is enough.

Answer

(a) Flexibility is the ability for software to be changed easily.


(b) We came up with the following. The list almost certainly isn't
complete and it could be much better written, but it's what came out
of our brainstorming so we left it as it is.
◦ Low coupling so changes don't have knock-on effects.
◦ High cohesion so functions that are closely related can get
changed together as a unit.
◦ Hiding implementation behind interfaces and contracts
because this keeps coupling as low as possible.
◦ Use component-based development so components are
pluggable/replaceable.
◦ Delegate details such as object creation to factories so clients
don't need to know them and coupling is kept low.
◦ Use layers. Low coupling and separation of concerns.
◦ Wrap legacy software (related to hiding functions behind
interfaces).
◦ Package functions as loosely coupled services.

Flexibility is a very important quality, not only in agile development where


frequent changes can be expected, but also for maintenance of delivered
systems.
Bass et al. (2012) describe four tactics for flexibility (which they call
modifiability). You should already be familiar with the first two from your
studies during the module and the answers to SAQ 10.
Tactic 1: Minimise coupling
Coupling is concerned with how much changing one part of the system
affects other parts. If coupling is low then a change in one place does not
have knock-on consequences for other parts of the system.

81
Unit 10 Building blocks and enterprise architectures

SAQ 11

List as many ways as you can think of to reduce coupling. You will
probably have come up with a number of them already when answering
SAQ 10.
Answer

We thought of these:
Use components that hide their implementation behind interfaces.
Use services that hide their implementation behind interfaces.
Use a layered architecture.
Use packages to group closely related elements.
Separate model from presentation.
Hide legacy software behind wrappers.
Delegate object creation to factories.
Use a registry to locate objects or services instead of having to know their
location.

Tactic 2: Maximise cohesion


Cohesion measures how closely the activities of a module are related to one
another. Module here could mean any kind of subsystem – a component, a
service, an object-oriented class and so on. If a module has high cohesion it
will group together a set of closely related activities and exclude unrelated
ones. Its activities will all share a common purpose.
If activities are together within a module they will be less isolated from one
another than if they are part of different modules. If a module cuts across
many different concerns it may need to be changed frequently, and a change
required to one activity in a module may have effects on otherwise unrelated
activities.
Tactic 3: Keep modules small
The idea here is fairly intuitive: if modules are small and a change involves
just a single module it will be relatively easy to make. However, we need to
make sure that we do not separate a group of activities that are likely to
change as a unit. Otherwise, if a change does need to be made, the number of
modules affected will have been increased.
Tactic 4: Bind as late as possible
A binding is simply an association between two things. For example:
. In an object-oriented programming language a variable name is bound to
an object when the object is assigned to the variable.
. A user interface uses a particular look and feel, so it is bound to it.
. In a service-oriented architecture a consumer finds a service from a
registry and binds to the service.

82
4 Architecture, quality attributes and tactics

. A symbolic name such as www.open.ac.uk is bound to a numerical IP


address (137.108.198.32 at the time of writing, 2014).
The longer we can delay any binding, the more flexible the system will be,
because a different choice remains available.
For example object-oriented programming languages typically allow the class
of the object to which a variable is bound to be decided at run-time, making
it possible to vary behaviour by replacing an object of one class by one
belonging to a different class.
Instead of the code of a user interface binding it to a particular look and feel,
the look and feel can be decided at run-time from a configuration file, which
can be changed as required. Even better, the creation of the look and feel can
be delegated to a factory, as discussed in Unit 9.
In a service-oriented architecture a service may be replaced by a different one
that meets the same contract. (In a similar way it may be possible to replace
a component by a different component, or a layer by a different
implementation, if the replacement fulfils the same contract. This is related to
low coupling of course and relies on the implementational details being
hidden behind the interface.)
The binding between a name and an IP address can be changed at any time,
meaning that it would be possible to move the server to a different numerical
IP address and simply rebind www.open.ac.uk to the new address, so clients
would be entirely unaffected!

4.5 Summary of section


In this section you learnt about the six-part model for quality attribute
scenarios. A source delivers a stimulus to an artefact operating within a
particular environment. The artefact generates a response, which can be
measured in some objective way.
You saw that for a given quality attribute we can make a list of the general
possibilities for each part of the model and then, to express a particular non-
functional requirement, we choose an appropriate value for each part of the
scenario. Examples of the general values and specific requirements were
given for two attributes, performance and usability, but the model can be
extended to any quality attribute.
Tactics for quality attributes are reusable solutions to the problem of
achieving particular qualities. You saw detailed examples of tactics for two
example attributes – performance and flexibility. You learnt that the main
tactics for performance are managing demand, managing system capacity, and
matching resources to demand. The main tactics for flexibility are minimising
coupling, maximising cohesion, keeping modules small, and delaying binding
for as long as possible.

83
Unit 10 Building blocks and enterprise architectures

5 Putting it all together – enterprise


architecture
Frameworks were discussed in In this section we look at a widely used framework for creating and running
subsection 4.2 of Unit 9. enterprise applications: Java Platform, Enterprise Edition (Java EE), provided
by Oracle. We are using this framework as an example, and you are not
required to remember the technical details of the code.
Software development at enterprise scale demands more than just a
disciplined application of basic design principles. Adherence to standards is
also essential, because it allows different organisations and manufacturers to
produce and market components that can be acquired and plugged together to
build new systems.
We outline how Java EE supports component-based development, giving a
simple example of an actual component. We demonstrate that the same
functionality can easily be delivered as a service as well, making it possible
to interoperate with a wide variety of clients.
Finally we explore one possible architecture for the hotel system, showing
how a single enterprise application created within Java EE can contain
examples of many of the reusable styles and patterns you have studied in
Unit 9 and in this unit.
Very large scale applications are generally like this. Even if they seem to
follow just one style overall, once we look more closely at the detail, a
mixture of styles and patterns emerges, each used where it is the best solution
to some particular problem.

5.1 Introducing Java EE


Java EE is built on top of the Java language and consists of an aggregation of
many technologies that have been standardised over the years and integrated
into the Java EE platform, which continues to evolve and grow as new
technology standards emerge. At the time of writing the current version is
Java EE 7.

Java Community Process (JCP)


The Java Community Process (JCP) was introduced in 1998 as an open,
participative process to develop and revise the Java technology
specifications, reference implementations and test suites. Since then, the
JCP programme has fostered the evolution of the Java platform in
cooperation with the international Java developer community.
Individuals or organisations can become JCP members by signing the
Java Specification Participation Agreement (JSPA). The JSPA is an
agreement between a company, an organisation or an individual and

84
5 Putting it all together – enterprise architecture

Oracle, setting out each Community member’s rights and obligations


when participating in the development of Java technology specifications
in the JCP.
While there are no mandatory obligations, the success of JCP is
predicated on members’ participation in a range of activities, including
providing feedback on proposed specifications, submitting specifications,
leading or nominating people to form expert groups or build
independent implementations.
The JCP website address is http://jcp.org/en/home/index.

A key driver for the development of Java EE was supporting application


distribution and portability. Enterprise applications developed in Java EE can
be distributed across computer networks and, by and large, they work on all
operating systems. This is possible because of the separation between Java
EE applications, their infrastructure and the operating system: many vendors
provide Java EE infrastructures for various operating systems and a Java EE
application can be deployed in any of these infrastructures.
Java EE is made up of many technologies. These include:
. J2SE (Java 2 Platform, Standard Edition). The standard Java language,
which Java EE is built on top of.
. EJB (Enterprise JavaBeans). A technology for reusable server-side
business components, called enterprise beans. The range of services Java
EE provides for EJBs includes persistence, distribution, security and
transaction processing. A typical Java EE application will involve a
number of EJBs each dedicated to specific tasks.
. Servlets, JSF (JavaServer Faces) and JSP (JavaServer Pages).
Technologies for constructing web pages and serving them to clients on
demand. Each distinct web page will correspond to one or more
components written using these technologies.
. JPA (Java Persistence API). A technology for accessing relational
databases.
. JMS (Java Message Service). A technology enabling software components
to communicate asynchronously with low coupling.
. JAX-WS (Java API for XML Web Services) and JAX-RS (Java API for
RESTful Web Services. Technologies for providing services that can be
accessed over the internet.
. JavaMail. A technology for sending email messages.
Support for component-based application in Java EE relies on the notion of a
container. A container is a special run-time environment provided by Java
EE that will provide components with supporting services, such as the
examples given in the list above for EJBs. Because the container provides the
supporting services, developers can focus on the application logic and leave
the complexity of the infrastructure to the container.

85
Unit 10 Building blocks and enterprise architectures

Java EE provides several containers, shown in Figure 10.

Client System Java EE server


Browser
Web Container
Applet JavaServer
Container Servlet
Faces
Applet

Application
Client
Container
EJB Container
Application
Client EJB EJB
Database

Figure 10 Java EE containers

The web container and the EJB container are normally part of the platform
supplied by an enterprise server, such as Oracle's GlassFish Server.
A browser just means one of the common web browsers, such as Firefox or
Chrome, that can send requests to a web server and display the response.
An application client is a component running on the user’s machine but able
to invoke operations directly on remote EJBs because it is running in a
special container that supports distributed access.
In addition to the containers shown above, a browser with a Java plug-in
installed provides an applet container. An applet is a small Java program
that is downloaded from a server and executes in a container consisting of a
Java plug-in installed in the client's browser.

SAQ 12

Java EE applications follow a layered architecture. Describe how the Java


EE containers and other elements shown in Figure 10 fit into a three-layer
architecture like the one described in Unit 1.
Answer

Browsers, applet containers, application clients, web containers, JavaServer


For more information about Java Faces and servlets are all in the presentation layer.
EE see The Java EE 7 Tutorial
The EJB container and EJBs are in the application domain, since EJBs are
(Oracle, 2014).
business components.
The database is part of the infrastructure.

86
5 Putting it all together – enterprise architecture

5.2 An example EJB


To illustrate EJBs we use a small example that converts between two The code in this section and the
common units of land area – the acre and the hectare (ha). The EJB is to be next is just so that you can get a
accessible remotely, and for this we have to first define a remote interface: feeling for how components and
services might be implemented in
package converter; practice. You are not expected
import javax.ejb.Remote; to remember the details.
If you are interested in trying the
@Remote
examples out we have provided
public interface ConverterBeanRemote { instructions on the module
String convertLandArea(double area, String unit); website but this is optional.
}

As you see this is much like any ordinary Java interface, the only difference
being that it imports javax.ejb.Remote, which is part of the Java EE API,
and then uses an annotation @Remote, which informs the EJB container that
this interface should be accessible from remote components.
This is the code of the EJB that implements the converter:
package converter;
import javax.ejb.Stateless;
@Stateless
public class ConverterBean implements
ConverterBeanRemote {
public String convertLandArea(
double area, String unit) {
if (unit.equals("acres"))
return String.valueOf(area*0.40468) + "ha";
else
if (unit.equals("ha"))
return String.valueOf(area*2.471) +
"acres";
else
return "Unit not recognised";
}
}

Again this is similar to any other Java class, except for the annotation
@Stateless. This instructs the container that ConverterBean is an EJB
and that it is stateless. In other words, it lacks any memory. Every time the
EJB's operation is invoked, even by the same client, it is completely
independent of previous invocations, all details of which are forgotten. This
is all that is necessary for a conversion operation, which simply accepts an
area and returns the result of converting it.
To use this class we simply deploy it to a running Java EE container: in
effect copy a compiled version of the class, together with some configuration

87
Unit 10 Building blocks and enterprise architectures

files, to a particular folder recognised by the container. We do not have to


create an instance of the class; that is taken care of by the container.
An EJB is a server-side component. A client-side component that can
communicate with it is an application client. Generally application clients
have a graphical user interface (GUI) but to keep things simple the following
code just displays results in a console. It submits three hard-coded operation
invocations and prints the return values.
package converter_client;
import converter.ConverterBeanRemote;
import javax.ejb.EJB;
public class Main {
@EJB
private static ConverterBeanRemote converterBean;
public static void main(String[] args) {
System.out.println(converterBean.convertLandArea
(25, "ha"));
System.out.println(converterBean.convertLandArea
(100, "acres"));
System.out.println(converterBean.convertLandArea
(50, "rood"));
}
}

This application client is a standard Java class, except that it imports the
remote interface of the converter bean, otherwise it would not know what the
provided interface of that component is.
It also uses another Java EE service – dependency injection. When the lines
@EJB
private static ConverterBeanRemote converterBean;

are executed, the container will automatically initialise the variable


converterBean with an instance of the ConverterBean class. The creation
and management of this EJB object are handled entirely by the container,
which ‘injects’ the necessary reference into the client.
In practice we would format the When the application client is run, the output is as follows:
output better than in this simple
example! 61.775000000000006 acres
40.467999999999996 ha
Unit not recognised

5.3 An example service


Service-oriented architecture and the implementation of services are huge
topics. All we have space for here is a short demonstration of how the
component-based functionality of the last subsection can be made into an

88
5 Putting it all together – enterprise architecture

interoperable service that uses standard communication protocols, rather than


being solely dependent on Java technology.
To do this we will implement the area converter as a RESTful web service. REST stands for representational
RESTful services follow a different philosophy from components. Recall that state transfer.
a component has a provided interface consisting of a set of operations special
to that component. To use a component a client obtains the component's
address by some means and then invokes operations on the component,
supplying arguments where necessary.
In contrast, RESTful web services use a uniform interface consisting only of
a set of standard methods which are always the same for every service. These
are the standard HTTP (hypertext transfer protocol) methods, used by web
browsers to communicate with web servers. The commonest of these is GET,
which is the request the browser sends to the server when you open a web
page. The request is sent to the URL of the web page and the server at that
web address responds with the information requested, which the browser then
displays.
How can a whole range of different services be provided by sending only
GET requests, you might ask? The answer is: by making each item of
information into a distinct resource with its own URI.
Although the client always sends a GET request, it can obtain any required
item of information by including the appropriate URI.
For an example of how this works in practice, consider the following call to
the converter bean component.
converterBean.convertLandArea(20, "ha")

If the same functionality is implemented as a RESTful service the


information becomes a resource with a unique address, built up by starting
with a root URL and adding path elements to represent the service required
and the arguments.
In the working example we developed when writing this section the root
happened to be:
http://localhost:8080/TM354-web/resources/

The path element for the service was chosen to be convert which is
appended to this:
http://localhost:8080/TM354-web/resources/convert

Then to convert a particular value, 20 ha for example, we append the area


and unit as further path elements, which gives
http://localhost:8080/TM354-web/resources/convert/20/
ha

This is the URL of the resource that represents the result of converting
20 hectares to acres. To obtain the required information all we will need to
do is send a GET request to this address.

89
Unit 10 Building blocks and enterprise architectures

Implementing the converter as a RESTful service


To provide a RESTful converter we take the same method as before and wrap
it differently:
@Path("/convert")
public class ConverterService {
@GET
@Path("/{area}/{unit}")
public String convertLandArea(
@PathParam("area") double area, @PathParam
("unit") String unit) {
if (unit.equals("acres"))
return String.valueOf(area*0.40468) + "ha";
else
if (unit.equals("ha"))
return String.valueOf(area*2.471) +
"acres";
else
return "Unit not recognised";
}
}

You should be able to see how the @Path annotations correspond to the path
elements discussed above. First,
@Path("/convert")

specifies the service. Then the elements that represent the area and unit are
specified by the statement
@Path("/{area}/{unit}")

The braces mark these as path parameters, which are to be replaced by the
corresponding elements in the actual URI the client uses when making a
request.
The @GET annotation on the convertLandArea method indicates it should
be called whenever an appropriate GET request is received.
The annotations @PathParam("area") and @PathParam("unit")
associate the method arguments area and unit with the corresponding path
parameters. When a GET request is sent to an address such as
http://localhost:8080/TM354-web/resources/convert/20/
ha

1 The path element convert causes the request to be directed to an


instance of the ConverterService class.
2 Within that class GET requests are handled by the method
convertLandArea.

90
5 Putting it all together – enterprise architecture

3 The elements 20 and ha get assigned to the arguments area and unit
respectively.
4 The method performs the conversion and returns the result to the client as
the response to the GET request.
To use the class we just deploy it to a Java EE web container. The container
will take care of creating an instance of the class and forwarding appropriate
GET requests to it.

The client side of REST


Once deployed, the service can easily be accessed from a browser which will
send a GET request to the URL and display the response (Figure 11).

Figure 11 RESTful service from browser

Notice that the browser, unlike the EJB client, is not aware of the Java
technology used at the server end. It simply sends a request and receives a
response, using standard protocols.
We can consume the service from a program as well as a browser. To
demonstrate how easy it is to interoperate with the converter service, the
following code shows a converter client written in a completely different
language. You shouldn't need to know any Python to get a general idea of
how the code works.
import urllib.request
root = "http://localhost:8080/TM354-web/resources"
service = "convert"
area = "25"
unit = "ha"
url = root + "/" + service + "/" + area + "/" + unit
response = urllib.request.urlopen(url)
print(response.read())

5.4 An architecture for the hotel system


In this subsection we explore what a Java EE architecture for the hotel
system could look like.

91
Unit 10 Building blocks and enterprise architectures

SAQ 13

Read the following information about the hotel system and then sketch on
paper a possible way of building it as a Java EE application, showing what
components would be involved, what containers they would run in and
what the communication is.
The hotel system will require three types of client: web clients
connecting via the internet, desktop applications installed in hotels,
and mobile clients using tablet and phone apps that connect to
RESTful web services.
Part of the system must deal with the core business processes, such
as reservations, room lettings, billing and so on.
The system will need to maintain persistent data about customers and
hotels.
Answer

We drew Figure 12. You may have added some extra details.

Java EE Server

Web Container
Browser
Servlets
Mobile
Client
JSF

RESTful services

Application
Client EJB Container
Container
EJBs
Application
Client Database

Figure 12 Answer to SAQ 13

SAQ 14

Look back at the architectural styles described in subsection 4.1 of Unit 9.


Which of the styles can you identify examples of in the Java EE hotel
system?

92
5 Putting it all together – enterprise architecture

Answer

We identified the following:


Call-return. When an application invokes a method on an EJB.
Layered. We have already discussed the fact that Java EE follows a three-
layer architecture. Although we said earlier that the web container is in the
presentation layer there is a slight exception to this because RESTful web
services provide business services and so should be regarded as being in
the application domain.
Client–server. When a browser sends a request to a web server (or a client
sends a GET request to a RESTful web service).
Data-centred. The database is a centralised store of persistent data the
structure of which is relatively stable, so this is an example of the data-
centred style.
Service-oriented. Although we don't have a complete SOA, the RESTful
web services can be considered as an example of this style.

SAQ 15

Suppose the management of the hotel chain decides it would like a way of
sending customers information about special offers. It would also like to
be able to send important bulletins to the application clients installed in the
hotels.
(a) Which two Java EE technologies mentioned in subsection 5.1 could
be used to meet these requirements?
(b) What architectural style are these an example of?
(c) Both these technologies run in the web container. Redraw your
sketch of the hotel system architecture to include them and the
associated communication.

Answer

(a) JavaMail and JMS (Java Message Service) respectively.


(b) Notification.
(c) Our answer is shown in Figure 13. Notice that the application
clients are now in communication with JMS in the web container, as
well as with EJBs. Web clients and mobile clients would receive
information about special offers via their normal email systems.

93
Unit 10 Building blocks and enterprise architectures

Java EE Server

Web Container
Browser
Servlets
Mobile
Client
JSF

RESTful services

JMS

Java Mail

Application
Client EJB Container
Container
EJBs
Application
Client Database

Figure 13 Answer to SAQ 15(c)

SAQ 16

(a) Which parts of the hotel system would you expect to change the
most often?
(b) Reread the discussion on tactics for flexibility in subsection 4.4 and
suggest some examples of the use of these tactics in the Java EE
hotel system.

Answer

(a) The part of the system dealing with core business functions can be
expected to change quite frequently in response to changes in
business rules.
The part of the system that constructs web pages is also likely to
change quite frequently as new features and page layouts are
introduced.
(b) We thought of the following examples of modifiability tactics in the
Java EE hotel system. You may have come up with different ones.
Minimise coupling
Java EE keeps coupling low in many ways, such as by:

94
5 Putting it all together – enterprise architecture

◦ using interfaces
◦ using loosely coupled messaging systems (JMS and
JavaMail)
◦ using loosely coupled RESTful web services
◦ separating concerns and placing them in different layers
◦ dependency injection which hides the details of creating
resources such as EJB instances.
Maximise cohesion
Java EE components of all kinds tend to have good cohesion because
they are generally written to be responsible for a small number of
closely related tasks. The ConverterBean and the
ConverterService are good examples of this, since they each do
just one thing.
Keep modules small
Java EE components with their dedicated focus are also usually small
and relatively easy to change or replace. The ConverterBean and the
ConverterService are again good examples.
Bind as late as possible
An example of this is the dependency injection in the application
client, where the container binds an instance of the ConverterBean
to the name converterBean only at run-time when the application
client needs to use the EJB object.

This concludes our look at Java EE and how it might provide an architecture
for the hotel system.
Actually implementing the system as a Java EE application is beyond the
scope of the module, but in Unit 12 we will look at how a working prototype
can be written using Java Platform, Standard Edition. This will let us explore
many important features of the detailed design, including how the design
patterns you learnt about in Unit 9 can be applied in practical situations.

5.5 Summary of section


In this section we introduced you to Java Platform, Enterprise Edition, a
framework for constructing large-scale systems.
Java EE includes many different technologies and we listed the main ones
and what each is used for. Java provides support for component-based
applications in the shape of several different types of container, special run-
time environments that offer services such as persistence, distribution,
security and transaction processing.
One particular type of component widely used in Java EE is Enterprise
JavaBeans, which typically provides business operations. You learnt about the
details of a small example of EJB to convert between two different measures
of land area.

95
Unit 10 Building blocks and enterprise architectures

You also learnt that Java EE provides support for developing service
providers and saw how the same conversion example that you met in
component form can easily be reprogrammed as a RESTful web service.
Finally we introduced a possible Java EE architecture for the hotel system.
This architecture uses a range of technologies, and contains elements from
several of the architectural styles you learnt about in Unit 9.

96
6 Summary

Summary 6
In this unit you have extended your knowledge of software architecture, how
architectures can be constructed from reusable elements, and how software
architects can design systems to meet quality attribute requirements.
We began with component-based development, which structures software by
combining reusable components that hide their implementation. A component
has a provided interface – what it makes available to other components – and
can also have a required interface – what it needs from other components. An
interface is more than a set of operations and includes all the conditions
necessary for a component to function correctly.
Then we looked at services, which are abstract units of business functionality.
Services resemble components in having a well-defined interface and hiding
implementation, but differ in being independent of any particular technology,
and being discoverable via a registry. We introduced the ‘find, bind and
invoke’ cycle and you saw how services can be combined in a service-
oriented architecture and the advantages of this.
We next discussed quality attribute scenarios, which provide a model for
documenting quality attribute requirements and measuring their attainment,
and gave detailed examples involving performance and usability. Tactics are
reusable solutions for meeting quality requirements and we looked at tactics
for two example qualities, performance and flexibility.
In the last section we introduced Java Platform, Enterprise Edition for
building large-scale systems and looked at the main technologies included in
Java EE. We introduced the concept of a container for component execution
that provides a range of standard services such as persistence, distribution,
security and transaction processing.
Java EE supports component-based development but also supports service
providers. We gave a simple example of an application presented first as a
component and then as a service. Finally we gave a possible Java EE
architecture for the hotel system, noting that it included elements from
several different architectural styles.
On completion of this unit you should be able to:
. discuss characteristics of software components and model simple
components
. explain how objects can function as components
. define service-oriented architecture and explain some of its advantages
. write and apply simple quality attribute scenarios
. explain the role of tactics in meeting quality requirements and discuss
their application in simple cases.

97
Unit 10 Building blocks and enterprise architectures

References
Bass, L., Clements, P. and Kazman, R. (2003) Software Architecture in Practice, 2nd
edn, Boston, Addison-Wesley.
Bass, D. L., Clements, D. P. and Kazman, D. R. (2012) Software Architecture in
Practice, 3rd edn, Upper Saddle River, NJ, Addison Wesley. Available at http://
proquestcombo.safaribooksonline.com.libezproxy.open.ac.uk/book/software-
engineering-and-development/9780132942799 (Accessed 2 December 2013).
Chung L. and do Prado Leite, J. C. S. (2009) ‘On non-functional requirements in
software engineering’ in Borgida, A. T., Chaudhri, V. K., Giorgini, P. and Yu, E. S.
(eds) Conceptual Modeling: Foundations and Applications, Heidelbery, Berlin,
Springer-Verlag, pp. 363-379.
Oracle (2014) The Java EE 7 Tutorial [online]. Available at http://docs.oracle.com/
javaee/7/tutorial/doc/home.htm (Accessed 22 October 2014).
Oracle (2014) Java Community Process [online]. Available at http://jcp.org/en/home/
index (Accessed 9 April 2014).
Parnas, D. (1972) ‘Information distribution aspects of design methodology’,
Proceedings of the 1971 IFIP Congress. Ljubljana, Yugoslavia, August 23–28 1971,
Amsterdam, Netherlands, North-Holland Publishing Company, pp. 339–44.
Sommerville, I. (2011) Software Engineering, 9th edn, Boston, MA, Pearson.

98
Unit 11 Product quality:
verification, metrics and testing
Contents
1 Introduction 103
2 Software quality 105
2.1 What is quality? 105
2.2 Software quality factors 106
2.3 Summary of section 110
3 Verification and validation 111
3.1 Consistency and self-consistency 112
3.2 Completeness 113
3.3 Summary of section 114
4 Testing 115
4.1 Test-driven development 115
4.2 Design by contract 119
4.3 General categories of testing 126
4.4 Strategies for creating test cases 134
4.5 Measuring complexity 146
4.6 Summary of section 152
5 Summary 154
References 156
1 Introduction

Introduction 1
Some of the most vital differences between agile development methods and
older plan-driven development methods stem from competing views about the
proper place of testing. One of the principal motivations for agile methods
was to counter the escalating cost of changes that occur late in the
development process. It was partly in an attempt to minimise such rising
costs that Beck (2002) and others developed extreme programming (XP).
One of the many insights of XP was the importance of moving testing from
the final stages of the development process and to redistribute testing
processes evenly throughout the development process as far as possible. The
aim was for feedback time from tests to be reduced from weeks or months to
minutes or seconds. Many of these insights on testing have been distilled into
methods for the test-driven development (TDD) of code (Beck, 2002),
which form part of agile methods but can also be applied separately.
TDD methods (Section 4.1 of this unit) go beyond the idea of simply
distributing testing throughout development. They further demand that, before
any code is written, tests should be written for that code to meet. Just enough
code should then be written to pass those tests – then further tests should be
written and so on. On each iteration all of the tests should be run. This
approach has the major advantage that as development proceeds, and after
any change, a complete suite of tests can be run at the press of a button
(subject to the length of time it may take to complete all the tests). Beyond
simply ensuring that code works, TDD methods may also be viewed as
making a distinctive contribution to the design process, as explored in this
unit. In practical terms, TDD eases the process of making changes to code at
late stages in development, simply because the accumulated tests provide
assurance that changes have not broken the code. In object-oriented contexts
this whole approach is generally relatively convenient to manage due to the
existence of unit testing frameworks such as JUnit.
Agile development is not the only relatively recent method to have had a
profound effect on testing, even where not fully adopted. Design by contract
(DbC) – discussed in several previous units – has introduced equally
distinctive practices and tools that have proved valuable in testing, even
where featuring only as part of other methods.
DbC calls for the rights and responsibilities of every method to be identified
in the form of explicit, executable assertions that must invariably remain true
at run-time. Languages such as Eiffel and Java support assertion
mechanisms (discussed in subsection 4.2 of this unit), which allow pre- and
postcondition encoding in methods, independently of program logic. Such a
mechanism provides a framework so that, during testing, pre- and
postconditions: In Java, assertions need not be
limited to pre- and
. may be checked at run-time postconditions. They can also be
placed at arbitrary places within
. have no effect on program logic method bodies.
. can be switched on or off globally – thus not impacting performance

103
Unit 11 Product quality: verification, metrics and testing

. when enabled, indicate their breach by throwing exceptions


. provide assurance that code changes have not violated invariant
requirements.
Pre- and postconditions, like the unit tests of TDD, are a form of executable
documentation. Unlike unit tests, they focus on invariant conditions rather
than on test cases. Some of the benefits of using assertions can be enjoyed
whether or not DbC is applied strictly, such as the use of assertions to
identify how and where problems arise at run-time.
Notwithstanding these interesting developments, all developers need to know
about a wide range of testing concepts, methods and practices beyond TDD
and DbC. There are many circumstances where those methods may not be
particularly applicable, for example projects that lean heavily on legacy code,
reuse large code components, emphasise user interfaces or involve databases
or concurrency. Equally many development projects mandate plan-based
development or a mixture of methods. Even when TDD or DbC are used in a
pure form, these methods cannot remove the need for several other kinds of
testing, such as usability testing, integration testing and customer acceptance.
Consequently, while encouraging testing at the earliest possible opportunity,
in this unit we cover a wide range of approaches to testing. Fortunately all of
these approaches are largely compatible and can complement each other well
during testing.
More generally in this unit you will consider verification and validation, two
central processes for achieving a quality software product. You will study
software quality and how it can be measured. Other important topics include
metrics, forms of testing and testing techniques.

104
2 Software quality

Software quality 2
This section begins with a short introduction to quality in the context of the
products and processes of the software life cycle. Quality is related to the
customer’s requirements, and this allows us to understand what factors affect
product quality, together with ways of measuring them.

2.1 What is quality?


There is a vast and highly competitive market for software. In many cases,
product quality – such as ease of use or reliability – can provide critical
commercial advantages.
But what, precisely, does ‘quality’ mean in the context of software?
Other things being equal, we would like all software to be of the highest
quality possible. However other things are not equal. Achieving high quality
takes time and can be costly. For example, think about Rolls-Royce or Aston
Martin, both of which are renowned for the high quality of their cars.
However neither company works in a mass market. Within a mass market
(where most software is sold), quality only needs to be sufficiently high,
otherwise its cost might exceed what the market will bear. Similarly, as
discussed in Unit 1, sometimes the critical factor is not the highest possible
quality, other factors such as time to market being more significant.
Software that is fit for its purpose and of sufficiently high quality is said to
be of appropriate quality. We shall use the term ‘quality’, in the context of
software, to stand for ‘appropriate quality’. One way of characterising the
notion of appropriate quality is to say that it is in conformance to the
requirements and expectations of the customer.
However a customer’s requirements for software can be expressed in many
ways. At their most formal, they appear in a requirements specification that is
part of the contract between developer and client. They can also arise and be
documented during the iterations of the software development process. In
either case a customer’s requirements are typically thought of as something
that is recorded in some way.
A customer’s expectations, on the other hand, may or may not be recorded. As discussed in Unit 2, there are
In some cases, some expectations can be gleaned from documented quality a variety of ways in which
standards. Some but not all such expectations stem from general customer requirements might be
recorded. These include formal
characteristics that are expected of professionally developed software. In templates or, in a more agile
other cases customer expectations may be unconscious and tacit until these approach, product backlogs or
expectations fail to be met in the delivered product. sets of user stories.
Despite the difficulty of being sure of a customer’s requirements in particular
cases, the above characterisation of quality is a good working definition for
practising software engineers, in that it allows assessment of quality using
measurable attributes of systems.

105
Unit 11 Product quality: verification, metrics and testing

So, for example, to establish whether quality is appropriate, we can check:


. how the product operates against what was both explicitly required and
implicitly expected by the customer
. that quality and development standards – whether in-house rules or ISO
9000 mandated practices – have been followed
. that best practice of software engineers, as professionals operating to
professional standards, has been followed during development.
In this unit, we shall concentrate on what our definition of quality implies for
a software product.

SAQ 1

Suggest three expectations that a customer might have of a software


product without perhaps being aware of them.
Answer

Three possibilities are:


. the product will not conflict with other software that they use
. the product will boost productivity
. the product will be simple to use.
You may have come up with others.

2.2 Software quality factors


In one of the most seminal papers on software quality, McCall and Cavano
(1978) introduced a long list of attributes and sub-attributes to help assess the
quality of software. This framework remains highly influential, and
contributed substantially to the ISO standard ISO/IEC 9126, which classifies
software quality.
McCall and Cavano’s attributes include such factors as correctness, reliability,
efficiency, integrity, usability, maintainability and many others. They dubbed
these attributes of software products software quality factors (SQFs). You
have already seen most of these factors in earlier units:
. characteristics of a ‘good software system’ (Block 1 Unit 1 Section 2)
. non-functional requirements (Block 1 Unit 2 Section 4)
. conformance testing (Block 1 Unit 2 Section 5)
. quality attributes (Unit 9, Section 2)
. quality attribute scenarios with measures that test whether requirements
have been met (Unit 10, Section 6).
As already noted, McCall and Cavano’s classification has been sufficiently
influential that it contributed to the international standard ISO 9126 Software
Engineering — Product Quality. This ISO standard adds the useful feature

106
2 Software quality

that it groups the various SQFs into six classes: functionality, reliability,
usability, efficiency, maintainability and portability.
In this unit, our key concern is not to attempt to craft philosophically
watertight definitions of the nature of quality, but to be clear about what
aspects of quality can be measured, how they can be measured and the value
of such measurements. The purposes to which measurements of quality can
be put include:
. determining whether fit criteria are met
. deciding how well the concerns of stakeholder groups have been
addressed
. measuring the response in quality attribute scenarios
. assessing the overall quality of a system
. comparing systems to see which has higher quality
. seeing whether changes have successfully improved quality.
McCall and Cavano divided up quality requirements (whether stemming from
the customer or from other sources) into three categories:
. product operation requirements: how the product will be used
. product revision requirements: how the product will be maintained
. product transition requirements: how the product will be modified for
different operating environments.
Rather than advocating as an aim the highest possible scores for each SQF,
regardless of cost, McCall and Cavano were clear about the need to decide
and measure what is needed for each factor according to the particular
requirements of any given case.
Let us now briefly review McCall and Cavano’s software quality factors,
grouped under the three requirement categories in turn.
SQFs affected by product operation requirements include:
. correctness: how well a system fulfils the customer’s overall objectives –
how well the software does what the customer wants
. reliability: the likelihood with which a system can be expected to perform
its intended function – how well the software does what it is supposed to
do
. efficiency: the level of computing resources (including time) required by a
system to perform its function – how well it runs on the customer’s
hardware
. integrity: the strength of measures to ensure that modification or deletion
of data by unauthorised persons (or by any other unintended means) does
not occur
. usability: the effort required to learn about, operate, prepare input for and
interpret the output of a system – how easy the system is to use.

107
Unit 11 Product quality: verification, metrics and testing

SQFs affected by product revision requirements include:


. maintainability: the effort required to deal with expected changes and to
understand, locate and fix errors in a system
. flexibility: the effort required to modify an operational system – how
easily the system can be changed while in service
. testability: the effort required to test a system to ensure that it performs
its intended function. Different kinds of testing may vary according to
how easy they are to adapt when requirements are revised.
SQFs affected by product transition requirements include:
. portability: the effort required to transfer the system from one hardware
platform and/or software environment to another – how easily the system
can be used on another machine should the customer change their
platform or should other customers require it
. reusability: the extent to which a system (or system component) can be
reused in other applications – how easy it is to reuse some of the software
to make future developments more cost effective
. interoperability: the effort required to couple one system to another – how
easy it is to interface the system with another, should the customer require
it.
Clearly for a particular development some SQFs may be more relevant than
others. For example, a web server will probably require high efficiency and,
depending on the nature of its contents, high integrity. A safety-critical aero-
engine controller on the other hand will require correct and highly reliable
performance, with efficiency a secondary concern, and no integrity demands
in the sense defined in Block 1 Unit 2.

SAQ 2

. Explain how increasing integrity within a system could affect


efficiency.
. Identify one other pair of SQFs that are not independent.
Answer

. Increasing integrity within a system means strengthening measures to


ensure that modification or deletion of data by unauthorised persons,
or by any other unintended means, does not occur. This might
involve the use of passwords to access certain data and an
authentication server to check a user’s identity, or it might mean that
network traffic needs to be encrypted and decrypted. Each of these
factors adds an overhead to processing, so efficiency is likely to be
reduced.
. Another pair of SQFs that are not independent is usability and
portability. For example, many of the features of the Apple
Macintosh that contribute to its reputation for usability are built into
its operating system. Applications that take advantage of these

108
2 Software quality

features are less portable to other systems, such as Windows or


Linux.

Primary software quality factors


Not all SQFs are of primary importance in all situations. For everyday
software products Tom Gilb (1988) has identified just four SQFs as being of
primary importance: correctness, integrity, maintainability and usability. Thus
when seeking a relatively uncomplicated measure of the quality of a typical
software product it is useful to focus on ways of assessing the levels of just
these four factors.
This can be done using various techniques, a sample of which we consider
below.
. Correctness. A popular measure for assessing correctness is defects per
thousand lines of code (defects per KLOC), where a defect may be defined
as a verified lack of conformance to requirements. Defects may be
identified by testers or reported by users of a software product after the
product has been released for general use. In the case of a large project
they may be counted over a standard period of time, for example one
month.
. Integrity. This is measured by considering the proportion of ‘attacks’ on a
product as opposed to bona fide uses. In cases where integrity is of great
importance, and different kinds of attack can be identified, it can be useful
to measure or estimate quantities such as the likelihood that an attack of a
given type will occur within a given time and the likelihood that an attack
of a given type will be repelled. If historical data is available that allows
these probabilities to be calculated with some accuracy, for example using
information from a log file, then it becomes possible to assign numerical
values in a simple way to the integrity of a system.
. Maintainability. Unfortunately there is no way to measure maintainability
directly, and so we must measure it indirectly. A simple measure is mean
time to change (MTTC), which is the average of the times it takes to
analyse a bug report, design an appropriate modification, implement the
change, test it and distribute the change to all users. In general, the lower
the MTTC (for equivalent types of changes), the more maintainable the
software product is.
. Usability: any system with a user interface and that will be used by
people other than the developers should be usability tested. Usability
testing involves users systematically trying out the user interface and the
system behind it – although for some purposes the system may be
simulated. There are also forms of evaluation such as heuristic review that
can be used to make substantial improvements to user interfaces without
involving users. For any system that depends on how users will interact
with it, user interface design and testing should be continual engineering
focuses. However the details of usability testing lie beyond the scope of
this module.

109
Unit 11 Product quality: verification, metrics and testing

2.3 Summary of section


In this section you studied the need for software products to be of appropriate
quality with respect to customer requirements and expectations. You reviewed
software attributes that contribute to quality, known as software quality
factors (SQFs), and their organisation into six classes by ISO standard. You
saw how four SQFs are of primary importance, and noted some techniques
for measuring them. In the next section you will study verification and
validation, the two essential means of judging software quality.

110
3 Verification and validation

Verification and validation 3


Verification and validation are ways of assessing whether a software product
(or any product for that matter) does what it is supposed to do, and does it
correctly. These processes are fundamental to quality. In this section we shall
study verification and validation, and how they are carried out. Testing is an
essential part of verification and validation and, as already emphasised, is
ideally performed throughout the software development process.
Traditionally, as noted in Block 1 Unit 1, validation is concerned with
whether the right product is being built, whereas verification concerns
whether the product is built in the right way. There is an IEEE Standard for
System and Software Verification and Validation – IEEE 1012-2012.
Loosely speaking, verification assumes that the current specifications are
correct and focuses on ensuring that development processes produce outputs
that meet the specifications. More specifically, verification tests the extent to
which the product conforms with the various, often evolving, system
descriptions designed to help produce it.
System descriptions can include any testable means by which the system is
more or less formally described. These may include testable stories, fit
criteria, unit tests, assertions, requirements statements, analysis and design
models, source code, executable code, system documentation and so on,
associated with any activity in the development of the software product.
Formally, verification is the process of checking that a given system
description S is self-consistent, and that system descriptions that are derived
from S are consistent and complete with respect to S. Verification is about
applying the right techniques in the development of S.
By contrast validation does not assume that either the specifications or
development processes are adequate. Validation focuses on ensuring that
outputs meet the needs, including implicit needs and expectations of
customers and stakeholders. These may never have been written down. For
this reason validation is generally harder than verification. Validation can
have far-reaching effects and can result in changes to specifications or
organisational processes, or both. Validation ultimately depends on
acceptance by customers.
From our definition verification of a system involves two tasks:
. ensuring that all system descriptions are self-consistent
. ensuring that all system descriptions are consistent and complete with
respect to those from which they were derived.
Likewise, from our definition validation comprises:
. ensuring that all system descriptions are consistent with the customer’s
requirements, including implicit requirements.

111
Unit 11 Product quality: verification, metrics and testing

3.1 Consistency and self-consistency


Two system descriptions that describe the same part of a system are
consistent if they make no contradicting statements about the part that they
describe. Consistency with a customer requirement simply means that the
requirement is satisfied.

SAQ 3

Give a simple example of two system descriptions that might contradict


each other.
Answer

There are numerous possibilities. One elementary example would be if a


structural model for a hotel reservation system indicated that a reservation
could be made for more that one room but the implementation only
allowed one room per reservation.

Example 1
The hotel booking scenario in SAQ 3 suggests a simple example of
In Block 2 Unit 8 Section 5 you consistency with customer requirements. If the customer requirement for a
saw several examples of hotel reservation system stated that single reservations must be able to cover
consistency checks. more than one room, then an implementation only allowing one room per
reservation would clearly be inconsistent with a customer requirement.

A system description may be inconsistent with itself. This is the situation in


which one part of the system description says one thing but is contradicted
by another part. Such lack of self consistency is most likely to happen in the
system documentation, in which contradictory claims of the system are often
made. However even an implementation need not be self consistent.

Example 2
There are many ways in which code for implementation might not be self
consistent.
For example, a variable address1 might be declared in candidate source
code as conforming to two incompatible types, as follows:
Address address1;
IPAddress address1;

This is self-inconsistent. In this particular case, the job of checking self


consistency is a task that can be performed by the compiler – such code
would fail to compile. There are many other inconsistencies that a compiler
usually would not catch, such as one developer assuming that a unique ID
generator is zero-based while another developer assuming it is one-based.

112
3 Verification and validation

SAQ 4

Why is it important for the customer’s requirements statement to be self


consistent?
Answer

If the customer’s requirement statement lacks self consistency, then either


the resulting system will be inconsistent or it will not satisfy the
customer’s requirements. The system builders can decide (implicitly or
explicitly) how to resolve the inconsistencies or, if the inconsistent
requirements affect different parts of the system and are not picked up by
the developers, the developers could inadvertently build the inconsistencies
into the product.

3.2 Completeness
In contrast to consistency, the concept of completeness is very simple to
grasp, at least in principle. It means that everything that should have been
‘said’ in a system description has been ‘said’. However looked at more
closely there are two different kinds of completeness. When the focus is on
judging whether one system description is complete compared with another
(usually earlier) system description, this is known as completeness
verification. By contrast, comparing a system description with customer
requirements is known as completeness validation. Despite its simplicity in
principle, validation of completeness can be very challenging, for it means
that every aspect of the customer’s requirements must be met by the system
description. Fit criteria and functional
requirements were introduced in
Early in the development process, it is worth reviewing the requirements Block 1 Unit 2.
statement for requirements that are impossible to verify or validate, with the
aim of either improving the requirements or removing them from the
requirements statement (after appropriate review). Because completeness is
such a difficult property to ensure, verification may tend to aim at analytical
completeness, where only requirements that have fit criteria are considered.
In the case of validation, in theory functional requirements, which are
requirements that describe what the system is to do, have fit criteria – the
system either does it or it does not. Unfortunately in practice functional
requirements do not cover every situation that might come up, so there are
usually missing criteria. Non-functional requirements, which describe
qualities of the system, often do not by default have binary fit criteria (yes/no
answers). Instead the developers must discuss the issues with the customers
to establish appropriate fit criteria.
During each stage of development, given user involvement, continuing
validation is typically able to reveal requirements that may have been
incomplete or unclear earlier on.
Given a reasonable set of user requirements, verification techniques applied
during each activity in the development process can ensure that deliverables
at each stage are consistent and complete.

113
Unit 11 Product quality: verification, metrics and testing

At each stage, you need to ensure that the deliverables from each activity are
consistent and complete with respect to the input to that activity. For
example, if you defined a set of use cases and built an analysis model as the
initial structure of the software solution, the classes in the analysis model
must be exactly those needed to support the realisation of those use cases.

Example 3
Most user manuals are not complete, in the sense that they do not describe all
the features of a product. For example, a word processor might contain many
features not fully described anywhere, and two or more features might
interact in unexpected ways. For example, many word processors contain an
outliner view that allows text to be displayed and edited in a structured form.
In outliner view, subheadings at different levels are typically progressively
indented, each level being further differentiated by a distinctive font style. In
some systems, users may get a surprise when trying to apply features such as
bolding, italicisation, font change and highlighting to text in outliner view.
Some such changes may be accepted, others not, and there may be further
inconsistency in how these appear on screen and when printed.

3.3 Summary of section


In this section you have studied the distinction between verification and
validation and a high-level view of how they can be carried with reference to
system descriptions. You have seen the role of consistency and completeness
in these two processes, and the special case of analytical completeness. In the
next section you will study testing, the main activity for verifying and
validating a software product.

114
4 Testing

Testing 4
The previous section gave an overview of how verification and validation can
be used to ensure the appropriate quality of software. When considered in
more detail, the principal activity for verifying and validating software is
testing. Even though testing is not infallible, it is an essential part of
developing software. Many approaches can be used to promote testing
throughout development. These include:
. prototyping (the rapid creation of exploratory software artefacts that are
discarded after evaluation)
. iterative approaches (where early software artefacts are built on rather
than discarded)
. frameworks such as the dynamic systems development method (DSDM) –
promoted by the not-for-profit, vendor-independent DSDM Consortium –
which documents best practice processes for iterative and incremental
development.
In this unit we focus primarily on two distinctive systematic approaches that
support testing throughout development – test-driven development and design
by contract. Later in this section we will consider wider approaches to, and
other kinds of, testing.

4.1 Test-driven development


Preparing for test-driven development – unit testing
In order to understand the practicalities of test-driven development, as a
preliminary step it helps to be aware of a key tool – unit testing. You may Some authors prefer unit testing
have studied unit testing in other modules, in which case you can go straight to be at the package level. We
to subsection 4.1.2. shall concentrate on the class as
the unit. Techniques for unit
In an object-oriented system the structural unit is the class. Unit testing is testing of packages can be easily
therefore usually performed on classes, with the focus on the systematic extrapolated from those for
classes.
testing of methods using test cases.
When programming in Java, JUnit is the most common tool for the creation
You will see a worked example
and management of unit tests. JUnit was originally introduced by Kent Beck
of JUnit in Unit 12.
and Erich Gamma and derived from Kent Beck’s earlier Smalltalk unit-testing
framework, SUnit. Since its inception JUnit has been immensely popular and
it is now integrated into most Java IDEs. There are now similar frameworks
collectively known as xUnit for most programming languages.
JUnit provides a framework that makes it easy to write and manage tests
using a standard format. JUnit provides a special test class, and JUnit
automatically subclasses it for each class to be tested. For example, to start
testing the class ExampleClass, you use JUnit to automatically create an
empty class TestExampleClass. You then populate the test class with a
series of test methods, typically one test case for each method of

115
Unit 11 Product quality: verification, metrics and testing

ExampleClass. Executing and managing the test methods is then an


automated process. Each time the tests are run, JUnit generates a report
listing that itemises which tests the software passed and which failed.

Test-driven development in a nutshell


In TDD, code is written in very small increments – of the order of no more
than five or ten lines of code. But crucially, no code is written without first
devising and writing an automated test case for each code increment. An
automated testing environment such as JUnit should be used. Each cycle
should typically take under five minutes. The basics of this approach are
simple to learn and apply. However it takes time and practice to apply the
technique effectively on larger projects. TDD is often associated with the
agile movement (although it predates it), but it can be readily applied as a
complement to most other approaches.
Tests with teeth: if your existing The essence of the cycle (illustrated in Figure 1) is as follows:
code already passes a proposed
test aimed at testing as yet 1 Decide on a code increment.
unwritten code for as yet non- 2 Decide on a test.
existent functionality, then a do-
nothing increment will 3 Write the test.
presumably also pass the test – 4 Run all tests, expecting the new test to fail (so that you know that the test
suggesting that your proposed has ‘teeth’).
test is vacuous as regards the
increment. 5 Write the code.
6 Run all the tests and succeed.

1 6
decide
code run all
increment tests

decide write
2 code 5
test

write run test


test predicting
failure
3 4

Figure 1 Six principal steps of TDD

116
4 Testing

Test-driven development cycle in more detail


Step 1 Decide on code increment
Decide on an appropriate increment of functionality to code next (but don't
code it yet).
Comment. This is generally the hardest step, for reasons that will become
clear later.
Step 1.1 Is this step likely to involve more than about five lines of code? If
so, go back and simplify the choice of increment or choose a different
increment.

Step 2 Decide on test


Decide on a test that will pass if the proposed code increment is present and
fail if it is absent.
Comment. This is generally the next hardest step.
Step 2.1 Is this test likely to involve more than about five lines of code? If
so, go back to step 1 or 2.

Step 3 Write test


Comment. Your test should avoid references to code internals. It should
restrict itself to the interfaces of relevant objects. (Note that since an
automated testing environment such as JUnit is typically used, the test will
generally be a new distinct method of a test class.)
Step 3.1 Is the code more complicated than expected? Consider going back to
step 1 and simplifying.

Step 4 Run test, predicting failure


Comment. This may seem pointless, but it is an essential check of the test.
(Will it really fail? Will it fail in the way expected?)
Step 4.1 If the class or method of interest about to be tested does not yet
exist then the test will not even compile. If predicted, this is an entirely
appropriate way for the test to fail.
Step 4.2 If the newest test does not fail, the test needs changing.
Step 4.3 If it fails in a way you do not expect, this typically demonstrates
that you had a faulty or incomplete understanding of your test or what you
were testing. In order to reflect your extended knowledge of possible faults,
generally the test will need improving or replacing.

Step 5 Write the code increment


Write the minimum needed to pass the test. Don't add extra functionality.
Sometimes in an early cycle this may involve just hard coding the answer.

117
Unit 11 Product quality: verification, metrics and testing

Step 6 Run all tests and ensure success


Run all of the tests and make sure the newest test succeeds. If the most
recent test fails and it is straightforward to fix the problem, fix it. If the
problem is not easy to fix, consider reverting and trying something simpler
instead of debugging.

To-do list
Sometimes, for example when coding a large-scale feature such as a pattern
or architecture, or when coding anything that turns out to need more than two
or three TDD cycles, or when in the process of coding you notice that
Refactoring is the reorganisation something needs refactoring, it can be useful to maintain a short to-do list
of code structure for reasons that can feed into the step 1 of future cycles. This list should be kept short.
such as clarity or maintainability,
while making no change to the
external behaviour. Benefits of test-driven development
TDD is claimed to bring many benefits, including the following:
. Test coverage: Test coverage is in some respects comprehensive, with
virtually all code having associated tests. These tests should all have been
run successfully throughout development. Finished code therefore already
has an extensive test suite.
. Regression testing and early discovery of errors: Many kinds of error are
discovered and corrected at the earliest opportunity. Changes that break
tests can be quickly identified and rectified.
. Executable documentation: The tests both show how the code should be
used and indicate what it should do by means of test cases.
. Beneficial effect on design: The writing of tests contributes to design as
well as to testing. TDD encourages clarity, modularity and a focus on
interface over implementation.
. Complementarity with DbC: TDD tests by test cases, whereas DbC tests
by invariants. These perspectives are usefully complementary.
. Promotes good code quality: TDD promotes sustained focus on quality,
design for testability and early and frequent refactoring.
. Inhibition of ‘featuritis’: Because addition of extraneous code is
discouraged during TDD cycles, creeping featuritis – the addition of
unnecessary features – is inhibited.

Limitations and pitfalls of test-driven development


TDD is not a panacea and has many limitations and possible pitfalls,
including the following:
. User interface: TDD does not readily apply to user interface testing, for
which it is better to apply techniques such as usability testing.
. Testing of applications integrated with databases: TDD alone is not
adequate for the comprehensive testing of databases.

118
4 Testing

. Multithreaded systems: TDD is not generally suitable for the testing of


multithreaded systems, as results may depend on the vagaries of timing.
. Customer acceptance: TDD cannot take the place of customer testing.
. Legacy systems and systems reusing large code components: If large
amounts of code are being reused and TDD was not applied when they
were being coded, then at best unit tests can be added retrospectively.
Such a retrofit would not constitute TDD, since the testing would not
guide the evolution of the code. In many cases, retrofitting unit tests to
large bodies of existing code may be impractical.
. Management support: If management chooses to penalise time spent on
writing tests during development, TDD is unlikely to flourish.
. Code whose requirements are liable to change greatly: Sometimes, for
example during scientific research or product development research, the
purpose of developing software may be to find out what the requirements
should be in some little-understood area or what happens in some poorly
understood situation. This is known as exploratory programming. In these
and other situations, requirements may change so extensively and
frequently that there is repeated wholesale invalidation of existing unit
tests. This would render TDD a hindrance rather than a help.
. Integration testing: TDD cannot take the place of integration testing, for
reasons discussed below in detail in subsection 4.3.
. TDD is only as good as the tests devised: Devising good tests and
effective test cases is an important art and one that applies with a scope
far beyond TDD alone. Because of this wider scope and importance, we
will defer the discussion of strategies for creating test cases until
Section 4.4, at which point you will have had a chance to become familiar
with wider aspects of testing.

SAQ 5

Give two reasons why it is useful to run a unit test before the relevant
code increment has been written?
Answer

(a) If the test unexpectedly already passes at this point, this


demonstrates that it is not a good test of the next increment.
(b) If it fails in an unexpected way, this demonstrates a faulty or
incomplete understanding of the test that needs to be addressed in
order to have a good grip on the code and test.

4.2 Design by contract


An alternative to TDD as described above can be found in design by
contract, as introduced in Unit 10. In this subsection we consider a worked
example of design by contract, focusing on framing a contract and translating
it into code.

119
Unit 11 Product quality: verification, metrics and testing

An example contract
Figure 2 shows an excerpt from the class diagram for a banking system. To
demonstrate the use of Java assertions we shall show how they can be used
to verify the pre- and postconditions of a withdraw operation in the class
Account.

Customer Account
0..1 1..*
balance : int
overdraftLimit : int
withdraw(amount : int)

Figure 2 Classes for withdraw operation

Using natural language the contract for the withdraw operation can be
described as follows.
If the withdrawal will not cause the customer’s overdraft limit to be
exceeded, decrease the customer’s balance by the amount of the withdrawal.
In the same way as we did in Unit 6, we may identify the following pre- and
postconditions for the contract.
Precondition: the amount of the withdrawal must be positive, and the
customer’s balance prior to the withdrawal, added to the customer’s overdraft
limit, must be equal to or greater than the amount of the withdrawal.
Postcondition: the balance of the customer’s account will have been
decreased by the withdrawal amount.

Writing assertions in Java


To write an assertion in Java we use the assert keyword. An assertion
statement takes the form:
assert boolean expression;

When the program is executed with assertions enabled, the boolean


expression will be evaluated. If it evaluates to true program execution will
continue normally. However if the boolean expression evaluates to false an
AssertionError is thrown and execution is halted.
The general idiom for using Java assertions to verify the pre- and
postconditions of a method is as follows:
method heading
assert boolean expression for preconditions
method body
assert boolean expression for postconditions

SAQ 6

Suppose the following constructor is included in

120
4 Testing

Account.
public Account (int aBalance, int anOverdraftLimit)
{
// initialise an account with a given balance
// and overdraft limit
assert aBalance >= 0 && anOverdraftLimit >= 0;
// body of constructor goes here
assert getBalance() == aBalance &&
getOverdraftLimit() == anOverdraftLimit;
}
(a) Explain the meaning of the pre- and postconditions using natural
language.
(b) If assertions are enabled and the following statement is executed
what will happen?
Account acc1 = new Account(200, -50);
(c) If assertions are enabled and the following statement is executed
what will happen?
Account acc1 = new Account(0, 200);

Answer

(a) The precondition verifies that the arguments aBalance and


anOverdraftLimit are both greater than or equal to zero. The
postcondition verifies that the variables balance and
overdraftLimit have been correctly initialised with the values of
the corresponding arguments.
(b) A value of −50 for the overdraft limit will mean the boolean
expression in the precondition assertion evaluates to false and an
assertion error will be thrown.
(c) The precondition is met and so a new Account will be created with
a zero balance and an overdraft limit of 200.

When to enable assertions


By default Java assertions are disabled. To enable them we use a command-
line switch –ea when the program is executed. While the software is under
test, assertions will normally be enabled. Once testing is complete assertions
will be disabled again, to avoid the overhead incurred in checking them.
However the assertions are still present in the compiled code and can be
enabled again if required for maintenance.

Assertions for the withdraw operation


Now we look at how to write the pre- and postconditions of the withdraw
operation using assertions. Account has two attributes represented by
variables balance and overdraftLimit, with getter methods that return

121
Unit 11 Product quality: verification, metrics and testing

the values of these attributes. The natural language description of the


precondition is:

The amount of the withdrawal must be positive, and the customer’s balance
prior to the withdrawal, added to the customer’s overdraft limit, must be
equal to or greater than the amount of the withdrawal.

For simplicity we have This translates into the following:


represented the balance as an
int. In reality we would define a public void withdraw(int anAmount) {
Money type. // precondition
assert anAmount > 0
&& getBalance() + getOverdraftLimit() >= anAmount;

Implementing the postcondition is more complex. The natural language


description of the postcondition is:

The balance of the customer’s account will have been decreased by the
withdrawal amount.

To check this we need to remember the balance at the start before the
withdrawal has been made. One approach would be to use a local variable,
like this:
public void withdraw(int anAmount) {
// precondition
assert anAmount > 0
&& getBalance() + getOverdraftLimit() >= anAmount;
int oldBalance = getBalance();
// body of method
// postcondition
assert getBalance() = oldBalance – anAmount;
}

However the statement


int oldBalance = getBalance();

will always be executed, even when assertions are disabled, although in that
case it will serve no purpose and is an unnecessary overhead. We would
really like a way to store the initial balance only when assertions are enabled.
In Java it is legal to declare a The solution is to use an ‘inner class idiom’ to capture the initial value. We
class inside a method. Such declare a new class local to the withdraw() method. This class has a
classes, called local inner classes, variable whose purpose is to capture the initial balance and methods that
are visible only within the
method.
implement the boolean conditions corresponding to the pre- and
postcondition.
We then arrange for an instance of the inner class to be created only if
assertions are enabled. This can be achieved by placing the expression that
creates the object within an assertion statement. If assertions are not enabled
this statement will not be executed and so no inner class object will be

122
4 Testing

created. Note that the definition of the class and its constructor and method
will be outside the assertion statement.
Using the inner class idiom for the withdraw() method results in the
following. We have called the inner class AssertionChecker but of course
it could be given some other name. Unless an instance of
AssertionChecker is
public void withdraw(final int anAmount) { successfully assigned to
class AssertionChecker { assertCheck the first
private int oldBalance; assertion will fail and execution
will halt. It follows that if the
AssertionChecker() { second assertion is reached
oldBalance = getBalance(); assertCheck is not null.
} Methods precondition() and
postcondition() can
boolean precondition() { implement conditions of
return anAmount > 0 whatever complexity is required.
&& anAmount <= oldBalance + As they are executed only if
assertions are enabled the
getOverdraftLimit();
overhead incurred can be
} avoided by disabling assertions in
boolean postcondition() { the production code.
return getBalance() == oldBalance – anAmount; The variable assertCheck has
to be declared outside the
} assertion statement because Java
AssertionChecker assertCheck = null; does not allow declarations inside
assert (assertCheck = new AssertionChecker()) assertion statements. However
assertCheck is initialised to
!= null
null rather than an instance of
&& assertCheck.precondition(); the inner class, thus minimising
// body of method goes here the extra work if assertions are
not enabled.
assert assertCheck.postcondition();
Methods in the inner class can
} use any variable or method in an
enclosing method or class, with
Notice that we have still not written the body of the method! This is in the restriction that variables used
accordance with the philosophy that we do not implement the body of a must be declared final.
method until we have defined a contract. Doing things in this order means This is why in the example
that when we come to write the body we have a well-defined way of deciding anAmount is declared final.
whether it is correct (to the extent that it matches the contract). If the
postcondition is true whenever the precondition is met then the method
matches the specification and is correct (up to the correctness and
completeness of the contract). In more practical terms, provided the method
matches the specification, then whenever the method’s preconditions are met
by a caller it is guaranteed that the postconditions will be true when it
completes.

What, no precondition?
Some methods, such as getBalance() and getOverdraftLimit() have
no precondition assertion. This does not mean they have no precondition but
that the precondition is treated as always true. In other words a client can
always invoke these methods. The assumption is that an Account object will
have been correctly initialised by the constructor and so it will always have a
valid balance and overdraft limit that can be returned by the getter methods.

123
Unit 11 Product quality: verification, metrics and testing

To put it another way, for some methods, for example getters, the effort that
would be needed to frame and implement effective preconditions would be
out of all proportion to the simplicity of the code concerned and the tiny
likelihood of error – it therefore makes sense to adopt the assumption that the
precondition is always true.
In general, however, writing a contract that is complete and correct may take
as much work as writing the program itself.

The inertial convention


So far we have thought of the postcondition from the perspective of what
change the method brings about. But we also require that it does not do
anything other than what is intended. For example, the withdraw() method
of Account should not alter the overdraft limit. We might explicitly include
this in the postcondition:
boolean postcondition() {
return getBalance() == oldBalance – anAmount
&& getOverdraftLimit() == startOverdraftLimit;
}

‘Inertial’ is by analogy with the However objects often have many components to their state, and what about
physical notion that something other objects in the system? Perhaps we should also specify that their state is
will not change unless acted left unchanged. If we attempt to list everything that should not change the
upon.
postcondition will obviously suffer from an explosion of complexity.
So instead we normally assume that, unless the postcondition explicitly
specifies something should change, it is deemed to remain the same. For
example if the postcondition of withdraw() does not refer to
overdraftLimit then we can assume it is unchanged. This is called the
inertial convention.

Conditions that can’t be expressed in code


There may be conditions that we can describe using natural language,
assumptions about the environment for example, that can't easily be
expressed as Java assertions.
If a requirement can't be expressed as an assertion written in code, it is still
good practice to include it informally. This can be done by including its
natural language version, as it would appear in the use case, as a comment at
the appropriate place in the method. For example,
The Mars Climate Orbiter was // Precondition: all lengths must be expressed in metres
lost because engineers confused
imperial and metric Although an assertion expressed as a comment cannot be executed it is still
measurements. useful as a way of documenting the contract.

Class invariants
So far we have been looking at assertions that describe the pre- and
postconditions of individual methods. There is a third kind of assertion that

124
4 Testing

applies at the level of the whole class and restricts the state objects of the
class are allowed to be in. This is the class invariant, which states a
condition that any valid object of the class should satisfy at all times from its
creation onwards.
A simple example from the Account class is:
overdraftLimit >= 0
This states that the overdraft limit should never be allowed to become
negative otherwise the Account is in an invalid state.
An example involving more than one attribute is:
balance + overdraftLimit >= 0
This expresses the rule that although an account may have a negative balance
the balance must not exceed the overdraft limit for that account.

Temporary infringement of class invariants


Although we stated above that a class invariant must be satisfied at all times,
we should have qualified this by saying at all times when its state is
observable by other objects. While an object is being changed by a method
its state may become temporarily invalid, as long as the state is valid again
by the end of the method.
This is mostly straightforward for systems in which there is only a single
thread of execution, because while the method is executing no other code can
be running to observe the state and when the method terminates the invariant
will be true again.
However, even with a single thread of execution, things can go wrong. If a
method that is in the middle of changing the state of an object calls another
method, the second method might then be able to observe the object in an
invalid state. The only solution is to write code carefully with awareness of
this issue, especially if multiple threads are involved.

Checking class invariants


If necessary, class invariants can be checked by using executable assertions,
in a similar way to pre- and postconditions. For example we could include
the assertion statement
assert getOverdraftLimit() >= 0;
at the beginning and end of a method, to verify that the invariant holds at the
start and continues to hold at the end.

Assertions in the constructor An exception is if the


Like methods, constructors may have pre- and postconditions. The major constructor passes a reference to
responsibility of a constructor is normally to create a valid object, so that this to other objects. This will
have the effect of publishing the
when the constructor completes, the object will have been correctly initialised new object before it is fully
and the class invariant will hold. initialised.

125
Unit 11 Product quality: verification, metrics and testing

It is easy to see that if the constructor has to initialise a series of variables


there must be points where the object being created is not yet in a valid state.
Fortunately because of the way constructors are executed in Java it is not
usually possible for another thread to witness the object in a partly initialised
state.

Exercise 1
Suppose you are writing a deposit() method for the Account class. The
natural language description of the method is:

If the amount being deposited is greater than zero increase the


customer’s balance by the amount being deposited.

(a) Identify pre- and postconditions for the contract in natural language.
(b) Implement the pre- and postcondition in code using the inner class idiom.
Solution
(a) Precondition: the amount of the deposit must be positive.
Postcondition: the balance of the customer’s account will have been
increased by the deposit amount.
(b) public void deposit(final int anAmount) {
class AssertionChecker {
private int oldBalance;
AssertionChecker() {
oldBalance = getBalance();
}
boolean precondition() {
return anAmount > 0;
}
boolean postcondition() {
return getBalance() == oldBalance + anAmount;
}
}
AssertionChecker assertCheck = null;
assert (assertCheck = new AssertionChecker()) !=
null
&& assertCheck.precondition();
// body of method
setBalance(getBalance() + anAmount);
assert assertCheck.postcondition();
}

4.3 General categories of testing


TDD and DbC are valuable approaches, but all developers need to know
about a wider range of other testing methods, concepts and practices. There
are several reasons for this. Firstly there are circumstances where TDD and
DbC may not be particularly applicable. These include projects that make

126
4 Testing

intensive use of legacy code or reuse large components, and projects where
there is a heavy emphasis on user interfaces. Some projects that involve
intensive use of databases or concurrency may also require alternative testing
approaches. Some development projects and organisations insist on plan-
based development, or some mixture of methods. Even where TDD or DbC is
used in a pure form, these methods cannot remove the need for several other
kinds of testing, such as usability testing, integration testing and customer
acceptance testing.
Consequently, while always encouraging testing at the earliest possible
opportunity, it is important to consider a wide range of approaches.
Fortunately all of these approaches are largely compatible and can
complement each other well.
Stepping back from a focus on TDD and DbC, there are four distinct
categories of testing that it is useful to distinguish in general when testing:
. Requirements-based testing draws on previously gathered or formulated
testable requirements to check that a system meets the customer’s
requirements. The final stage in this form of testing is acceptance testing.
. Usability testing refers to testing of the user interface.
. Developmental testing is a term that refers to all of the testing carried
out by the team developing the software. It is useful to distinguish
between developmental testing at three different levels of scope – unit
testing, integration or component testing and system testing.
. Regression testing is any form of testing during development or system
maintenance that systematically checks that fixing one bug has not
introduced others.

Requirements-based testing
Requirements-based testing consists largely of acceptance testing, which is
performed by the customer and after which (all being well) the system is
accepted. We will return to this in the section below on developmental
testing.

Usability testing
If a system has a user interface and users who are not the developers then
usability testing is essential. Many products fail because of problems with the
user interface. If a user interface is designed in such a way that people do not
like it, make errors, find it slow or difficult to learn, care taken on other
aspects of quality may be pointless.
Broadly speaking, usability testing involves systematically trying out the user
interface (and the system behind it or a simulation of the system) with users.
However there are also various forms of expert review (such as heuristic
evaluation) that can be used to make substantial improvements to user
interfaces without involving users.

127
Unit 11 Product quality: verification, metrics and testing

In the same way that testing should be distributed throughout the process,
user interface testing should not be left to the end of the process. For any
system with any kind of complexity in how users will interact with it, user
interface design and testing should be a key engineering focus. However
usability testing lies beyond the scope of this module.

Developmental testing
Developmental testing is essentially a term for grouping other categories of
testing rather than as a distinctive approach in its own right. Developmental
testing refers to all testing typically carried out by the development team (not
just implementers). It is useful to distinguish between developmental testing
at three different levels of scope – testing at the unit level (already discussed
above under DbC and TDD), integration testing (or component testing
where working with pre-existing components) and system testing.

Integration testing
Integration testing is essential in anything other than the smallest projects. It
builds on unit testing and DbC by testing units in combination – essentially,
partial builds of a software system are tested. Integration testing involves
checking that unit or assertion-tested classes interface correctly together. In
the case of software using a procedural language, there can be a need for a
considerable amount of integration testing. However in the case of software
developed using an object-oriented language, the structuring of the classes
and packages means that integration testing is less of a burden.
There is however one major problem with integration testing that is specific
to object-oriented software, which is due to the complexities of dynamic
‘Dynamic binding’ here means binding caused by inheritance and polymorphism. The problem, illustrated in
that static inspection of the code Example 4, is due to the possibility of subtle coupling relationships between
may be insufficient to determine classes being combined with variations in behaviour due to polymorphism.
exactly what code will execute at
run-time when a message is sent
to an object held by a particular
variable. Such a situation can Example 4
arise because the object could Consider a base class, PlaneFigure, with three subclasses: Triangle,
belong to any one of a number of Circle and Line. We might define an abstract method draw() in
subclasses of some given class
(inheritance), or more generally PlaneFigure to indicate that instances of the subclasses should be able to
to any one of a number of classes render themselves to the screen, while Triangle, Circle and Line are free
that share a common interface to implement draw in class-specific ways (see Figure 3).
(polymorphism). Integration may
cause hitherto untested variations Now suppose that three different classes A, B and C each have a method
to occur. design(PlaneFigure aFigure). Let us further imagine that each of these
methods includes the code fragment aFigure.draw, asking for the message
draw() to be sent to the figure aFigure. At run-time, when an instance of
A, B or C is sent the message design(aFigure), aFigure might stand at
that time for an instance of any of three classes, Triangle, Circle or
Line. Consequently at run-time the draw() method could be drawn from
any of those three classes, thus nine possible run-time combinations exist of
calling class and called class (see Table 1). To help make the next part of the
argument clear, we have also provided an alternative illustration of this

128
4 Testing

PlaneFigure
draw() draw() is an abstract
method of PlaneFigure

Triangle Circle Line


draw() draw() draw()

Triangle, Circle and Line are three implementations


of draw() each local to a subclass

Figure 3 Class PlaneFigurewith three subclasses Triangle, Circle and


Line

ninefold set of possibilities, but using a different graphical style (see


Figure 4).

Table 1 Possible run-time combinations of calling and called class

Possible calling classes, of instances able Possible called classes, to which


to respond to the message instances of aFigure might belong

design(PlaneFigure, aFigure)
A Triangle Circle Line
B Triangle Circle Line
C Triangle Circle Line

A B C

aFigure.draw

aTriangle.draw aCircle.draw aLine.draw

Figure 4 Possible resolution of the message aFigure.draw In Figures 4, 5, 6 and 7 an arrow


represents invocation – the
arrow between class A and
Should we test all of the combinations? Conceivably errors in the aFigure.draw means that
Triangle.draw method could be uncovered only when the calling object is aFigure.draw is found in the
code of a method of A. However,
an object of class C, but not when called by A or B. This might happen, for because of inheritance, when A
example, if because of the requirements of the system, classes A and B need invokes aFigure.draw on an
draw only right-angled triangles and therefore this is what is tested, but C object aFigure that is actually a
draws triangles with arbitrary angles. In this case it is only by testing the Triangle (perhaps by
combination of C with Triangle that we discover the error. assignment, e.g. aFigure =
aTriangle), then the method
When errors are dependent in this way, the ideal testing strategy is to test all that is executed is aTriangle.
combinations. In this simple case, we could test all nine combinations and so draw.
be sure of testing the erroneous combination. Theoretically this is the safest

129
Unit 11 Product quality: verification, metrics and testing

testing scheme as it is the only strategy for ensuring that all combinations of
invoking and target classes are tested together, therefore being sure of testing
dependent errors. This is illustrated in Figure 5.

A B C

aTriangle.draw aCircle.draw aLine.draw

Figure 5 The theoretically best polymorphic testing strategy, in which all


combinations are tested

In the general case it is sensible to try to reduce the testing load in the
presence of inheritance and polymorphism, while still retaining validity of the
testing strategy. This can be achieved by testing only a subset of the
combinations.
There are two main strategies. The first assumes that any errors in the
invoking object and the target object are independent between invoking and
target classes, and means that, for the above example, we need choose only
three representative subcases, such as those illustrated in Figure 6.

A B C

aTriangle.draw aCircle.draw aLine.draw

Figure 6 A polymorphic testing strategy in which errors are assumed to be


independent between invoking and target classes

However the assumption that errors are independent is not always safe. A
second strategy, which balances the need to test independence while not
requiring full testing, is to choose one of A, B and C (say A) and test it with
all of Triangle, Circle and Line, and choose one of Triangle, Circle
and Line (say Circle) and test it with all of A, B and C. This is illustrated
in Figure 7. Note that the combination of A with Circle is in both groups of
tests but of course only needs to be carried out once.

130
4 Testing

A B C

aTriangle.draw aCircle.draw aLine.draw

Figure 7 A polymorphic testing strategy in which errors are not assumed to be


independent between invoking and target classes, but the testing load is lessened
by careful choice of test cases

SAQ 7

Given n classes A1, …, An, each of which uses the method foo
implemented in m classes C1, …, Cm, all of which are subclasses of parent
class C, calculate how many tests will be required using the following
approaches:
(a) the safe approach (Figure 5)
(b) the minimal approach (Figure 6)
(c) the balanced approach (Figure 7) to integration testing.
For this case which would be the most appropriate choice?
Answer

(a) m × n
(b) max(m, n)
(c) m + n − 1
m + n − 1 is not much bigger than max(m, n), so the balanced approach
might as well be used in preference to the minimal approach. However
m × n is generally much bigger than both m + n − 1 and max(m, n). So if
the safe approach were used in preference to the balanced approach the
testing load could increase dramatically.

In the case of component-based development, integration testing may take a


slightly different form. Component-based development, as described in
Unit 10, can dramatically reduce development times. Part of the reduction is
gained since the unit testing of components is not necessary, as long as the
components are trusted (that is, they have been thoroughly tested as part of
another development). Integration testing can be based on the assumption that
only run-time behaviour involving a trusted component that crosses a trusted/
untrusted boundary needs to be tested. This is the case when, for example, an
untrusted component invokes a method in a trusted component and vice
versa.

131
Unit 11 Product quality: verification, metrics and testing

System testing
Typically system testing is the next and highest scope of testing that is
carried out by the development team. System testing consists of checking that
a completed software system performs in accordance with its requirements
specification, in the form of previously gathered or formulated testable
requirements.
System testing forms a comprehensive testing set that checks for
conscientious software system development. Because of its complexity and
importance, in large software development organisations system testing may
be organised by the quality assurance department and may be carried out by
independent validation and verification teams not involved in the
development of the system.
According to Beizer (1996), system testing should comprise the following
generic tests:
. user-command testing (or operator testing) tests all user commands
from the point of view of tolerance of syntax errors and data input errors
. interface and protocol testing if the system communicates with other
systems in the outside world, tests its interaction with the communication
system
A popular term for starting a . start-up and initialisation testing tests the system’s ability to be started in
system is ‘bootstrapping’, which a working hardware/software configuration – in the case where there are
comes from the idiom ‘pulling many combinations of hardware/software, all configurations should be
yourself up by your bootstraps’
meaning to do something by your
system tested individually
own efforts. . restart testing tests the ability of the system to recover from errors of
internal state
. performance testing tests that the system meets all specified operating
requirements for speed, number of concurrent users permitted, and so on
. stress testing tests that the system can operate reliably at the limits of
each of its resources – for example to make web server simulate the
accesses of hundreds or thousands of users all at the same time to see if it
can cope with the load
. security testing tests that the system does not offer opportunities to
breach security
. acceptance testing is performed by the customer and after which, all
being well, the system is accepted.
Acceptance testing
We will focus in particular on Beizer’s last category – acceptance testing – as
it plays a key role in the process of validation. It relates to requirements-
based testing, mentioned briefly above. The mix of tests that constitute
system testing is chosen by the customer. The tests are generally performed
on the completed software by quality assurance personnel, in conjunction
with the customer, in the operational environment. Its purpose is to check
that the user requirements have been satisfied by the final code. On the basis

132
4 Testing

of the correct functioning of the tests that make up acceptance testing, the
customer formally accepts the software. For this reason, acceptance testing is
called a formal test. If an acceptance test fails, the customer has the right to
ask the developer to correct the software and to repeat all tests on the
corrected software.

SAQ 8

Are there any situations in which system testing should be carried out by
the implementers of a system?
Answer

Probably the only situation where this is appropriate is when the project
team is small. In small teams, one person might play the part of
requirements engineer, designer, implementer, tester and maintenance
engineer.

SAQ 9

What do you think is the relationship between system testing and


acceptance testing?
Answer

In general, the same tests will be carried out during acceptance testing and
system testing. System testing is an in-house activity and a customer need
never know how system testing went – any bugs can be dealt with before
the customer sees them. Acceptance testing, on the other hand, is
conducted with much more at stake – the customer can accept or reject a
system based on its performance at acceptance testing.

Regression testing
As noted in earlier units, regression testing is designed to ensure that new
changes to software have not broken parts of the software that were
previously working. As discussed earlier in this unit, both TDD and DbC aim
to build in a high degree of regression testing. However not all development
projects follow these methodologies or employ them strictly. Furthermore
regression tests are needed in aspects of the development process not within
the scope of TDD and DbC.
Depending on the particular development method being used, regression tests
will be needed at all of the previously discussed levels – unit, integration and
system. Regression tests are of great importance during developmental
testing, but they are just as important during maintenance subsequent to
system delivery.

SAQ 10

Why should regression testing be necessary even after the customer has
accepted the product after acceptance testing?

133
Unit 11 Product quality: verification, metrics and testing

Answer

Acceptance testing is the process of showing that the software meets the
customer’s requirements, not that there aren’t bugs in the code. In fact,
given that a system is put into use, bugs that require fixing are almost
certain to be found after acceptance testing. In addition, the system will be
maintained, with functionality added and changed, leading to a requirement
for regression testing.

SAQ 11

Use the following phrases, which describe four kinds of testing, to fill the
gaps in the following three sentences.
usability testing, requirements testing, security testing, regression testing
. TDD and DbC are valuable but not comprehensive tools for
_________________.
. TDD has ________________ built into it.
. DbC and TDD cannot substitute for thorough _______________ or
_________.
Answer

. TDD and DbC are valuable but not comprehensive tools for
requirements testing.
. TDD has regression testing built into it.
. DbC and TDD cannot substitute for thorough usability testing or
security testing.

Exercise 2
Which of unit, integration, system and acceptance testing are parts of
validation and which are parts of verification?
Solution
Unit and integration testing concentrate on whether parts of the system
perform according to their specifications, answering the verification question
(have we built the system correctly?). System and thus acceptance testing
focus on showing that the customer’s requirements have been met, answering
the validation question (have we built the right system?). Note however that
there are no hard and fast distinctions. Unit testing can be used to
demonstrate that a component satisfies a customer’s requirements – thus
viewed as validation – and system testing can be used to demonstrate that a
system operates according to specification – viewed as verification.

4.4 Strategies for creating test cases


One of the oldest and most general approaches to testing is to select some
function of the software, to gather or create some test data that the function
uses, predict the results that should be expected from running or using the

134
4 Testing

function and then to check whether the software conforms to expectations. If


not, then either we have misunderstood what the function was supposed to
do, or more likely we have found a bug that needs to be fixed. As you have
seen, TDD and DbC both have their own approaches to this process, but it is
vital to consider this process more generally, for example when the need
arises to test existing software for which neither unit tests nor explicit
contracts have been or are likely to be developed. When testing such
software, it is generally impractical to test software for every possible input
condition. A range of techniques is therefore used to attempt to find a
relatively small set of test data that is in some way representative. Although,
as you will see, various sophisticated techniques are available to help us
choose such representative data, there is no guarantee that testing will
uncover all the bugs.
A key emphasis when following this approach is to help find as many bugs
as possible (or at least to find the most important bugs) given the resources
available for testing. A primary motivation is that by finding and fixing bugs
software quality can be improved, but this is not the only motivation. Testing
can also improve the customer’s (and developer’s) confidence in a software
product. Statistical techniques can be used to model the patterns of bugs
found by testing different products, in order to estimate how many bugs there
are likely to be in a particular product before testing starts. Armed with the
knowledge of how many bugs have been found – and fixed – through testing,
we can estimate how many bugs remain to be found. Such statistical
estimation can be used to present independent arguments to the customer that
the delivered software meets specified quality requirements.
Moreover this kind of testing can improve the testing process itself. A cost–
benefit analysis of ‘bugs found’ versus ‘time taken’ can be performed to
determine when testing should stop under the law of diminishing returns.
Furthermore a detailed examination of where bugs arise in code has shown
that programmer-induced errors occur most often where code complexity is
high. Used in combination with complexity measures (which you will study
in the next subsection), testing can be optimised further by concentrating
testing effort where the code is most complex.

Example 5

Figure 8 plots data collected by John Musa at Bell Laboratories in 1979. The Although this seminal data is old,
data comes from the testing of a small component of a real-time command- it has been well analysed and
and-control system for which Musa was project manager. Each increment understood. Exactly the same
lessons are applicable to today’s
along the x-axis represents a successive run of this system. The height of systems.
each column shows how long (rounded to the nearest second) the system ran
before failure on that run. As can be seen from the plot, many runs lasted
less than a second. After each failure, attempts were made to debug the code
and the system restarted.
Bev Littlewood (1992) has pointed out that there are some striking features
of the data that are typical of software failure data. Generally there is obvious
improvement taking place in the reliability, since the longest times between

135
Unit 11 Product quality: verification, metrics and testing

Figure 8 Time between failures plotted against run number.

failures tend to improve as we progress through the data, as Figure 9


illustrates.

90 000

80 000

70 000

60 000

50 000
Time (s)

40 000

30 000

20 000

10 000

0
0 20 40 60 80 100 120
Number of errors

Figure 9 The growth in reliability for the data of Figure 8, obtained by plotting
when failures happened over time. In this figure, time is accumulated over all of
the runs.

Given the periodic occurrence of runs with ever-longer times between


failures, the average number of errors per unit time is driven down. However

136
4 Testing

though in these two senses the reliability increases over time, the time
between failures is still very variable right to the end, and there is no
guarantee of the execution time in the next run being better than zero at any
point. For example, Figure 8 shows that towards the end of the plot, after
about 42 000 seconds of cumulative testing, there is still a 0, that is a failure
within half a second of restarting the system.
So even without complex statistical justification you can see how testing
allows us to predict reliability. The data in Figure 9 shows that there were
131 failures during the testing process. With statistical estimation it is
possible to predict with a fair degree of certainty that there are probably
around 10 to 20 or so bugs remaining in the system.

SAQ 12

Try making rough-and-ready statistical predictions by extending a curve.


Continue the trend of Figure 9 by extending the curve. Can you make any
rough predictions about the number of errors that were originally present
in the system?
Answer

The curve you have drawn may become near vertical at around 140–145
errors. If this is the case, then this indicates that the time between failures
becomes very large in that range, meaning there are few errors left to be
found. Consequently, the number of errors originally present was
somewhere around 140–145.

Devising strategies for choosing test cases forms a vital part of validation and
verification. In the next two subsections, we shall describe how this activity
fits into two very general categories of testing techniques – black-box testing
and white-box testing.

Black-box and white-box testing


The essential difference between these two categories of testing technique is
as follows:
. In black-box testing, test cases are designed by looking at the
specification (that is, requirements, high-level design and external
interfaces) of the system to be tested – this relates to earlier discussions
of testable requirements in Block 1 Units 2 and 3, and discussions in this
unit of TDD and DbC.
. In white-box testing, test cases are designed by looking at the detail of
the implementation of the system to be tested.
TDD encourages an emphasis on testing via interfaces (in part as a way of
encouraging good interface design) and DbC encourages a focus on
adherence to contracts rather than a focus on the particular way that
functionality is implemented. Hence both TDD and DbC effectively focus
more on black-box testing than white-box testing (although not entirely, as
shown in the section on white-box testing below). However there are many

137
Unit 11 Product quality: verification, metrics and testing

situations in which white-box testing can be important, for example because


of the ways in which test cases are designed. Black-box and white-box
testing have complementary roles in the testing process, and it is important to
have a grasp of both.
. Black-box testing is used to test that each aspect of the customer’s
requirements is handled correctly by an implementation. Black-box testing
‘sees’ a system through its specification.
. White-box testing is used to check that the details of the implementation
are correct. White-box testing ignores the ‘big picture’ of the requirements
and instead looks to detailed designs to check that the system does what it
is supposed to do correctly, ideally representatively testing all paths. Of
course, as well as checking coverage of paths, white-box testing must
check that outputs are correct!
Sometimes both terms are used to refer to particular forms that such tests
took historically, when procedural languages dominated. Present-day systems
are more typically object-oriented and have methods whose behaviour varies
depending on the state of objects. Black-box testing techniques construed in
this narrow sense are not always suited to object-oriented systems developed
strictly using TDD or DbC. However in countless other situations – and in
any situation when using the more general sense outlined above – black-box
testing techniques are important.

Black-box testing techniques


Black-box testing techniques consider relationships between inputs and
outputs. This means either their functional relationships (that is, how one is
turned into the other) or the efficiency with which outputs are produced.
The techniques that can be used in black-box testing are characterised by the
fact that they can use only information available from the specification in
order to develop test cases. This means that to produce test data for a system
or subsystem only the defined relationships between inputs and outputs can
be scrutinised.
A quintessential black-box technique is partitioning (also known as
equivalence partitioning) combined with boundary testing (also known as
fence testing), which focuses on producing test data at the boundaries
The input data space is the set of between partitions of the input data space (or input domain). The input data
all values that method arguments space is partitioned into subdomains, where a subdomain is a set of input
can take. values that require the same type of processing to be performed. Subdomains
are obtained by the technique of case analysis, which determines, for each
user-perceived function of the (sub)system, the subdomain that results in that
Defensive programming is function being performed. Boundary testing is based on the observation that
essentially a design philosophy
that says preconditions should common errors are often caused by a data item being just one out, or, for
always be checked. Each example, a loop being executed one too many or one too few times – such
operation is responsible for errors are most visible at the boundaries of the input data space.
making sure that it is safe to
proceed. The checking is placed The strategy for choosing partitions may vary depending on the methodology
very close to the code that used to develop the software. For example, if you have designed your
depends on it. software using defensive programming, you will need to produce partitions

138
4 Testing

that cover all of the possible values that your arguments can take. For
example, to test a method to compute the square root of an integer, you
might partition the input space into two sets: integers less than zero and
integers greater than or equal to zero. The idea is that you need to check that
your code does something sensible with negative values – for example,
produce an error report – as well as taking the square root of non-negative
values. By contrast, under DbC you need to test methods only for values that
are valid according to the method’s preconditions. For example, under DbC
your square root method might expect the client to filter out negative values.
You will then need to test only for values greater than or equal to zero. Thus
quality code developed defensively requires more testing effort than DbC.
Other strategies can be applied more generally. For example, in random
testing test data is randomly generated within the input data space. Random
testing is very good at generating data a human tester would not think of, as
well as being cheap in terms of tools support. Its main disadvantage is that
for large programs the amount of test data that needs to be generated can be
very large and results cannot always be easily validated.
In error guessing the most unexpected (or perhaps the most bizarre) test data
that can be thought of are presented to the program unit. Some people have
an intuitive flair for creating test data that reveals errors. Although this
technique is informal, it cannot be ignored – it has achieved considerable
success in the past!

Using partitioning for black-box testing


Assume that we have been given a class (package or other subsystem) to test
and that the basic operations for the class have been determined from the use
cases. Test data needs to be chosen to show that the class performs those
operations according to the use cases.

Step 1 For each method in the class determine


the input data space
Using the use cases or other parts of the UML system description, the input
data space for each method of the class can be determined.

Example 6 (part 1)
In an airline reservation system, a Booking class allows an operator to update
a particular booking using an update method that has two inputs (command
and flightNumber) if a summary of the flight is required, and three inputs
(command, flightNumber and seatNumber) if reservation or cancellation of a
seat is required. The inputs are as follows:
. command – one of reserve, cancel or summary
. flightNumber – an integer in the range 1 to 999
. seatNumber– an integer in the range 1 to 450.

139
Unit 11 Product quality: verification, metrics and testing

The input data space for the update method thus consists of all possible
combinations of three values taken from {reserve, cancel}, {1, 2, …, 999}
and {1, 2, …, 450} together with all possible combinations of two values
taken from {summary} and {1, 2, …, 999}.

Step 2 Partition the input data space into


subdomains
Once the input data spaces for the class’s methods have been determined,
they must be partitioned into a set of subdomains using case analysis. Our
partitioning has assumed that the software has been designed and
implemented using a DbC approach, meaning that the caller is responsible for
not passing out-of-range data.

Example 6 (part 2)
When the reserve command is entered together with a seatNumber and a
flightNumber, a seat is reserved. When the cancel command is entered
together with a seatNumber and a flightNumber, a seat reservation is
cancelled. When the summary command is entered together with a
flightNumber, the total number of seats booked is displayed. These three
cases correspond to three distinct user-perceived functions of the airline
reservation system and lead naturally to a partition of the input data space of
the update method into three subdomains, as shown in Table 2.

Table 2 The subdomains for the Booking class

User-perceived function Subdomain


Reserve a seat command = reserve
1 ≤ flightNumber ≤ 999
1 ≤ seatNumber ≤ 450
Cancel a seat command = cancel
1 ≤ flightNumber ≤ 999
1 ≤ seatNumber ≤ 450
Summarise seats booked command = summary
1 ≤ flightNumber ≤ 999

Thus, for example, the ‘reserve a seat’ user-perceived function is exercised


by messages of the form (for an object of class Booking, aBooking say):
aBooking.update (reserve, flightNumber, seatNumber)
where flightNumber is between 1 and 999 inclusive and seatNumber is
between 1 and 450 inclusive.

140
4 Testing

Step 3 Test all subdomains given by the case


analysis
Once the input data space has been partitioned into subdomains by case
analysis, black-box testing can take place by choosing test data for each
subdomain. This test data should include not only typical (or expected) data
in the middle of each subdomain, but also values at the extremes of the
subdomain and close to the extremes (to detect errors that may arise if the
programmer has used the wrong comparison operator).

Example 6 (part 3)
Table 3 shows suitable test data for the summary command.

Table 3 Test data for the summary command

Command flightNumber
summary 1
summary 2
summary 500
summary 998
summary 999

The extreme values for testing the functionality of the summary command are
1 and 999, so we test for the correct processing of these values, along with 2
and 998 (which are values near the extremes) and for 500, the value in the
middle of the subdomain.
One major advantage of this three-step black-box testing strategy is that it
allows all possible user-perceived functions to be tested. However for many
(sub)systems the number of subdomains can be enormous, and hence the
effort involved in testing them all can be so large as to be prohibitive.

SAQ 13

Use the strategy for black-box testing described above to choose a good
set of test data for the cancel command discussed in Example 6 (part 2).
Answer

We need to test for each flightNumber in Table 2, and for each such
flightNumber we need to test extreme, near-extreme and central values of
the seatNumber data. Five suitable test values for the seatNumber data are
1, 2, 200, 449 and 450. A suitable set of test data for the cancel command
is shown in Table 4.

141
142
Table 4 Test data for the cancel command

command flightNumber seatNumber command flightNumber seatNumber command flightNumber seatNumber


cancel 1 1 cancel 500 1 cancel 999 1
cancel 1 2 cancel 500 2 cancel 999 2
cancel 1 200 cancel 500 200 cancel 999 200
cancel 1 449 cancel 500 449 cancel 999 449
cancel 1 450 cancel 500 450 cancel 999 450
cancel 2 1 cancel 998 1
cancel 2 2 cancel 998 2
cancel 2 200 cancel 998 200
cancel 2 449 cancel 998 449
cancel 2 450 cancel 998 450
Unit 11 Product quality: verification, metrics and testing
4 Testing

White-box testing techniques


If TDD or DbC has been rigorously followed, then it can be argued that there
is no obvious need for white-box testing. However not all development
projects follow TDD or DbC, or they may be followed only incompletely or
imperfectly. In any case, white-box testing may be a useful form of cross-
checking in cases where parts of the code are critical or it may be applied to
identify and remove redundant code. It is fair to question how much any
methodology is to be trusted, particularly, for example, when dealing with
safety-critical software. Additionally it could be argued that white-box testing
can be applied at the integration level based on design documentation.
Where it is used, white-box testing can be used in ways that concentrate on
well-known sources of developmental errors. For instance, logical errors on
the part of the programmer are more likely to linger in code that is not often
executed, for example in code that deals with special cases or initialisation
methods. Here we consider just one white-box testing technique, basis-path
testing. We shall concentrate mainly on choosing data that exercise all of the
paths, but when actually testing you will need to check outputs and make
sure the code meets the specification.

Basis-path testing
Basis-path testing was developed originally by Tom McCabe in 1976, and is A method might have
based on his cyclomatic-complexity metric, which counts the number of unreachable statements if some
independent paths through a method body (discussed further in combination of conditions can
never be satisfied. This typically
subsection 4.5). Basis-path testing ensures that all reachable statements in a happens when code is added to
method are tested at least once. The metric provides an indication of the deal with errors that are
maximum number of tests that need to be performed in order to traverse violations of the method’s
paths such that each reachable statement and branch in a program is executed preconditions or are violations of
at least once. (It might be that fewer tests could achieve this, for example postconditions of the methods it
uses.
two if statements might be controlled by the same condition.) Of course
testing each path once may not be sufficient. More tests may be required if Note that it might not be
possible to construct test data
the statements in several independent paths need testing more thoroughly such that all conditions evaluate
because of the interactions within the method, such as an if statement to false. For example, the method
sometimes modifying a variable used later in the program, perhaps in another might contain both:
if statement or in a for loop. The selection of the paths to test is driven If (a) …
both by coverage (the basis paths) and by the details of the method itself and
(internal interactions). If (!a) …
One way to determine basis-path test cases is as follows. First select data that In this case you treat each of the
mutually exclusive paths as a
will exercise the straight-through path, that is all loop and if conditions straight-through path.
evaluate to false and only default cases are selected in switch statements.
Then find data that deviates from the straight-through path at the first
decision point. The process is repeated for each subsequent decision point in
the program, varying the flow in each case.

143
Unit 11 Product quality: verification, metrics and testing

Ensuring testing is adequate


Problems with black-box testing
A large amount of data has been collected that suggests that black-box
testing, used in isolation, suffers from major drawbacks. In one study black-
box testing was performed on over 40 000 lines of program code from a real
software project. The study measured the proportions of individual statements
that were exercised by the black-box testing. As shown in Figure 10, fewer
than half (44.5 per cent) of the simple assignment statements and only around
a third (35.1 per cent) of the decision points were tested. The results indicate
poor coverage levels when a black-box testing strategy alone is pursued.

100%

55.5% 64.9%
not covered not covered
test thoroughness

50%

44.5% 35.1%
covered covered

0%
coverage of assignments coverage of decision points

Figure 10 The thoroughness of black-box testing

Moreover, although the process of generating test data for black-box testing
is straightforward, a major inadequacy of black-box testing is revealed if a
tested method’s operation depends for its behaviour on the internal state of an
object. As an example suppose that the Booking class in Example 6 includes
an attribute available to indicate whether a seat is available or not, but that
this attribute is not visible through the interface to the method – and so will
not be known of by a black-box tester. The behaviour of the reserve method
should be different when the seat is already reserved (and so available is
false) from when it is not. In black-box testing however we must ignore the
internal detail of the class and so any inconsistencies between the values of
available and other aspects of the objects would necessarily be missed.
There are two ways around this inadequacy. The first way can create a great
deal of extra work for the tester – not only must case analysis be done for
each command, but also all possible sequences of commands should be
tested. For instance, the sequence reserve a seat, cancel the same seat should
be run, as should reserve a seat, cancel a different seat. Clearly the number of

144
4 Testing

test cases increases dramatically if this testing strategy is used. The second
way, which is easier, is always to conduct black-box testing in tandem with
white-box testing.

Problems with white-box testing


However white-box testing is not without its own problems. The main one is
that white-box testing alone, by concentrating on the code of an
implementation, may not reveal customer requirements that have been
omitted in this implementation. Again the solution is to combine black-box
and white-box testing.
Another problem with both black-box and white-box testing is that, for
comprehensive testing of large systems, immense quantities of test data are
required. Often the only way to succeed in managing testing is to use
automated tools. Many aspects of testing become routine when using tools,
whereas they were generally considered a chore before such tools existed. For
instance, a tool can easily and automatically do a detailed change analysis, so
that much regression testing can be automated.

Exercise 3
Give an example where black-box testing will test something that white-box
testing would miss, and one where white-box testing will test something that
black-box testing would miss.
Solution
Because black-box testing takes its test cases from the specification, it is
likely to pick up the following sorts of errors that white-box testing would
miss (this is not an exhaustive list):
. operations required by the specification but not provided for by the
implementation
. errors between the interfaces of two classes
. errors in the transformations between internal states of a class, if these
affect input/output behaviour
. performance errors, in which system performance is found to be wanting
. system initialisation and termination errors.
On the other hand, in looking inside the implementation, white-box testing
will pick up the following sorts of errors that black-box testing would miss:
. the sequences of method calls in the body of a method that fail to perform
the correct function
. boolean conditions in if statements, while loops, etc. incorrectly
expressed
. loops that terminate incorrectly
. relationships between methods and attributes of classes that are incorrect.

145
Unit 11 Product quality: verification, metrics and testing

Exercise 4
A weather-recording system records wind-speed data, rainfall data and
barometric pressure, and sends summary data into a computer network. If the
wind-speed data is represented as an integer between 0 and 110, the rainfall
data is represented as a floating point number between 0.0 and 150.0
(significant to 1 decimal place) and barometric pressure is represented as an
integer between 800 and 1200, what is the input data space?
Solution
The input data space is the set of triples of values taken from the sets {0, 1,
…, 110}, {0.0, 0.1, …, 150.0} and {800, 801, …, 1200}.

4.5 Measuring complexity


Today computing is firmly established as an engineering discipline.
Computing systems of immense complexity are built and generally, with
notable (and very well publicised) exceptions, work well. That these complex
systems work as well as they do is testimony to the skills of the software
engineers who build them and to the techniques that they use. However,
historically these techniques have often been based on individual judgement
rather than purely scientific or mathematical rules. In order to go beyond such
subjectivity, we need recognised sets of metrics that allow us to make
objective comparisons. Measuring software complexity is also important in
determining where to focus testing effort.

Measuring system complexity


There isn’t room in this unit to consider in detail the many metrics available.
Instead, we shall focus on some of the more important ones. In particular, we
shall look at some of the techniques that have developed for the measurement
of system complexity and their use in object-oriented systems.

Lines-of-code metric
In this unit, complexity means A simple measure of the complexity of a system description is given by
structural complexity (how counting the number of lines in the description. When the system description
complicated the program code is) is a piece of code, the metric produced is called the lines-of-code (LOC)
and not execution complexity
(how much time/memory is
metric.
required to run the program). Traditionally the LOC metric has been claimed to provide accurate
measurements of complexity. The justification is that if we assume that errors
are distributed randomly in a program then more lines of code mean more
errors. However there are other factors working against it as an accurate
measure: for instance, should comment lines be included in the line count? In
some situations, the LOC metric does perform well – in an early study, Curtis
et al. (1979) found that the LOC metric is a useful predictor of the number of
errors for small units of code (in the body of a short method, for instance),
and can also be a good predictor of maintenance effort.

146
4 Testing

McCabe’s cyclomatic-complexity metric


Tom McCabe (1976) introduced the very popular – and still widely used –
cyclomatic-complexity metric. Since 1976 the cyclomatic-complexity metric
has been used to measure the complexity of software systems developed in
non-object-oriented languages such as Fortran, Pascal and C. It has also been
found to be applicable to object-oriented languages such as Java and C++.
McCabe’s cyclomatic-complexity metric measures the complexity of a
method by counting the number of independent paths through a method body.
A path is a trace of the statements in the method actually executed, given
some input conditions and ignoring all iterations except the first. Two paths
are independent if they differ in at least one statement or expression.
Consider the program represented by Figure 11, with the letters a to l
representing statements in the program.

b c

d e f g

h i j

k l

Figure 11 Independent paths in a program

To compute the metric, find the number of independent paths, taking into There are tools available that
consideration the branches (ovals with more than one arrow exiting them). In when given some source code,
the example in Figure 11 there are four: abdhk, abeik, acfjl, acfgfjl. will calculate the cyclomatic-
complexity metric.
Although counting the paths might sound daunting, there is a simple way to
calculate the number of independent paths through a piece of code – by
counting the number of decision points. A decision point is where a choice
can be made during execution and so gives rise to different paths through the
code. Decision points arise through if statements and while, do-while and
for loops. A single switch or try statement can also add many more
decision points.
Suppose you are given a method body and are asked to calculate its
cyclomatic complexity. Cyclomatic complexity is always counted from 1:
given that a method body starts and completes its execution, this defines one
independent path through it (often called the straight-through path). An if
statement makes a decision based on the evaluation of its boolean condition.

147
Unit 11 Product quality: verification, metrics and testing

It can therefore divert from the straight-through path and so each if


statement in the method body adds one to the cyclomatic complexity.
Similarly each while, do-while and for loop adds a single decision point.
Each switch statement adds one decision point for each of the cases it tests
for, excluding any default case, which is considered to be on the straight-
through path. Each try statement adds one decision point per catch block,
but any finally block is not a decision point as it will always be executed.
In Java the logical ‘and’ (&&) and ‘or’ (||) operators use short-circuit
evaluation. This means that they only evaluate their second operand if it is
needed to determine their result. For example, if the first operand of the ||
operator evaluates to true, the overall result will be true regardless of the
value of the second operand. For this reason, the operators && and || each
add a decision point. The relevant decision is whether or not the second
operand needs to be evaluated.
The cyclomatic-complexity metric has been found to be an accurate predictor
of the number of errors in method bodies – the higher the complexity as
predicted by the cyclomatic-complexity metric, the higher the likelihood of
errors. As a simple rule of thumb, a cyclomatic complexity of 10 or more is a
hint that a method body might beneficially be restructured into two or more
simpler methods (if appropriate to do so).

SAQ 14

Should the cyclomatic-complexity metric be used to measure the


complexity of an object-oriented software system?
Answer

Because the cyclomatic-complexity metric is based on decision points,


which are present only in methods, it is ‘blind’ to the class-structuring
mechanisms that are available in object-oriented system descriptions. As
much of the complexity of an object-oriented system is held in the class
structure, applying the cyclomatic-complexity metric to a whole system
would not therefore be appropriate.

SAQ 15

Compare the complexities of the following two pieces of code using the
LOC and cyclomatic-complexity metrics. What conclusions can you draw
about the relative complexity of the code?

Code A Code B

int i = 1; int j = 0;
while (i <= 5){ int i = 2;
playACard(i); j = i;
if (playerHasWon(i)) j = j + i;
break; j++;

148
4 Testing

i++ System.out.println(j);
} System.out.println(i);

Answer

Each piece of code has seven lines. The complexity according to the LOC
metric is therefore the same. Code A has cyclomatic complexity 3 whereas
code B has cyclomatic complexity 1, which suggests that code A is the
more complex of the two. This supports an intuitive view that the structure
of code A appears more complex than that of code B.

Object-oriented metrics
It is generally accepted that object-oriented systems require two levels of
complexity metric: one to measure method complexity, the other to measure
the complexity of the class structure. In fact, because one of the structuring
mechanisms for object-oriented systems – inheritance – is so radically
different from that of procedural languages, new complexity metrics have had
to be developed.
Chidamber and Kemerer (1994) are in the vanguard in this area and have
provided a suite of complexity metrics for use specifically with object-
oriented systems, which mainly measure structural complexity in terms of the
interaction of classes and inheritance. These metrics are as follows:
. Depth-of-inheritance-tree (DIT) metric. For a given class, DIT is defined
as the maximum number of hops we can make, starting at the class in
question and moving up the inheritance tree, until reaching a class or
interface from which we can go no further. We start with 0 and add 1 per
hop.
The motivation for the DIT metric is that the deeper the inheritance tree
the greater the number of classes that must be examined to find what
methods the given class has inherited, so DIT is a measure of how
difficult the class is to understand.
In Java, the DIT of the class Object, the superclass of all other classes, is
0 and the minimum for any other class is 1 because it must take at least
one hop before Object is reached.
For a single inheritance programming language the DIT is the same as the
number of ancestors a class has, but this is not true in languages like C++
that support multiple inheritance. Starting from JDK 1.8 Java also became
a multiple-inheritance language, because interfaces can now define
concrete methods and a class can implement multiple interfaces.
. Coupling-between-objects (CBO) metric. For a given class, CBO is
defined as the number of relationships the class has with other classes.
The more relationships a class has – and so the higher the value of this
metric – the more difficult it is to understand the use of the given class.
. Number-of-children (NOC) metric. For a given class, NOC is defined as
the number of immediate children for that class. This metric is a measure

149
Unit 11 Product quality: verification, metrics and testing

of the number of classes that will be affected by changes to a given


parent class.
. Response-for-a-class (RFC) metric. Precise definitions can vary, but one
of the most common definitions is that, for a given class, RFC is defined
as the size of the response set for the class, which consists of all the
methods and constructors of this class (including methods inherited from
superclasses) together with all the methods and constructors that are
invoked within this class on objects of other classes. Like DIT and CBO,
this metric measures the number of methods needed to understand a class.
Classes with high values for this metric will be more difficult to
understand than classes with low values.
◦ Some people and tools count constructors as methods, and others
don’t. We recommend that they be counted. Do not count catch
clauses, even though they look and behave somewhat like methods.
. Lack-of-cohesion-in-methods (LCOM) metric. For a given class, LCOM
measures its cohesiveness. Ideally, a class should represent a single
domain concept, reflected by the fact that the attributes of a class should
be closely related. LCOM is defined as the number of pairs of methods
(comparing each method with every other method) that do not make
reference to the same attributes minus the number of pairs of methods that
do. If this number is negative LCOM is zero. In highly cohesive classes,
methods will manipulate the same attributes.
. Weighted-methods-per-class (WMPC) metric. Given a class, WMPC
measures its complexity of behaviour. It is defined as the sum of the
cyclomatic complexities of each method of the class. This measure stands
in contrast with a simple count of the number of classes in each method.
In effect, in the case of WMPC the cyclomatic complexity of each method
is a weighting used to indicate its complexity. For a class as a whole, the
relationship between this metric and complexity is that a class with many
simple methods may be as difficult to understand as a class with a few
complicated methods.
Chidamber and Kemerer (1994) have provided evidence that classes with
CBO values greater than 7 or RFC values greater than about 50 account for
the majority of problems in object-oriented systems.
One advantage of Chidamber and Kemerer’s metrics is that, other than the
WMPC metric, they can usefully be applied very early in the software
development process. For instance, you can take a UML class diagram and
apply the CBO metric to measure its complexity. This is a very promising
use of object-oriented metrics and means that object-oriented system analysts
and designers can feed complexity information into the development process
very early on.

SAQ 16

Look again at the definition of the DIT metric. When calculating the
metric do you think that classes from the Java API should be included? Do

150
4 Testing

you think that API classes should be counted in Chidamber and Kemerer’s
other metrics?
Answer

Chidamber and Kemerer’s metrics measure complexity, and API classes


add to complexity. Hence they should be counted in all metric calculations.
For this reason, the Java documentation will be very useful when
calculating these metrics.

SAQ 17

The WMPC metric measures the complexity of a class in terms of the sum
of the cyclomatic complexities of its methods.
(a) Consider a class with ten methods, whose WMPC value is 40. How
confident can you be that this class is not too complex?
(b) If the WMPC value of a class with ten methods is over 100, how
certain can you be that this class is too complex?

Answer

For individual methods, a cyclomatic complexity of 10 or more should be


regarded as a hint that the method is too complex. In the case of classes, a
little more thought is needed.
(a) For a class with ten methods, a value for the WMPC metric of 40
would typically suggest acceptably low class complexity. However
although nearly all of the ten methods might be acceptably simple,
one or two might be unacceptably complex.
(b) By contrast, a complexity of greater than 10 × 10 = 100 is a fair
indication that the behaviour of the class is too complex.

Exercise 5
The following piece of code is a method for equality testing from the java.
util.Arrays class. Calculate the cyclomatic complexity for this method by
filling in the right-hand column of Table 5, cumulatively.
// Equality Testing
/**
* Returns true if the two specified arrays of longs
are
* equal to one another. Two arrays are considered
equal if both
* arrays contain the same number of elements,
* and all corresponding pairs
* of elements in the two arrays are equal.
* In other words, two arrays are equal
* if they contain the same elements in the same
order. Also,

151
Unit 11 Product quality: verification, metrics and testing

* two array references are considered equal if both


are null.
*
* @param a one array to be tested for equality
* @param a2 the other array to be tested for
equality
* @return true if the two arrays are equal
*/

Table 5 Code from java.util.Arrays

Line number Code Cyclomatic complexity


1 public static boolean equals
(long[] a, long[] a2) {
2 if (a==a2)
3 return true;
4 if (a==null || a2==null)
5 return false;
6 int length = a.length;
7 if (a2.length != length)
8 return false;
9 for (int i=0; i<length; i++)
10 if (a[i] != a2[i])
11 return false;
12 return true;
13 }

Solution
The table should have the entries in Table 6, giving a cyclomatic complexity
of 7.

Table 6 Calculation of cyclomatic complexity

Line number Cyclomatic complexity


1 1 (starts at one)
2 2 (if statement)
4 3, 4 (if statement, ||)
7 5 (if statement)
9 6 (for loop)
10 7 (if statement)

4.6 Summary of section


You studied how test-driven development and design by contract can be used
in testing, how they contrast with each other, how they relate to other forms

152
4 Testing

of testing, their limitations and situations in which they may not be altogether
suitable.
TDD and DbC are valuable approaches, but all developers need to know
about a wider range of testing categories and approaches. You studied broad
categories of testing – requirements-based testing (including acceptance
testing), usability testing, developmental testing (including regression testing
and system testing) – as well as considering system testing in more detail.
You studied two main techniques for making test cases, black-box and white-
box testing. Black-box testing provides evidence that a software system meets
its requirements, whereas white-box testing provides evidence that the detail
of the implementation is correct. Both black-box and white-box testing have
problems individually, especially in testing object-oriented systems. A
balanced testing strategy will include aspects of them both.
You studied how one of the software metrics, complexity, is measured
through lines of code, cyclomatic complexity and a variety of object-oriented
measures, particularly the Chidamber and Kemerer metrics.

153
Unit 11 Product quality: verification, metrics and testing

5 Summary
In this unit you studied quality and its relationship to the products of the
software development process. You saw the importance of concentrating on
producing products of appropriate quality – products that are fit for their
purpose and of sufficiently high quality that they meet the customer’s
requirements and expectations. A number of software quality factors and
metrics were introduced as ways of measuring whether a software product is
of appropriate quality.
The unit introduced verification and validation as ways of assessing the
quality of a software product, and you saw that testing is the most important
way of achieving verification and validation. You studied how test-driven
development and design by contract compare and contrast with each other,
and with other forms of testing, as well as their limitations.
The testing process – comprising unit, integration, system, acceptance and
regression testing – as applied to object-oriented systems was described. You
studied the two main techniques for software testing – black-box and white-
box testing – and saw that, for object-oriented systems, a sensible testing
strategy combines both techniques. You studied how one of the software
metrics, complexity, can be measured in a variety of ways.
On completion of this unit you should be able to:
. define quality and describe how the need for quality arises
. describe how quality relates to the products of the software development
process
. describe how the customer’s requirements and those from other sources
can make demands on the qualities of a software system
. explain how software metrics allow the estimation of values for software
quality factors
. define verification and validation, and explain how they can be achieved
. describe testing, its relationship to verification and validation, and what it
can (and cannot) achieve
. describe test-driven development and a short version of the TDD cycle
. describe design by contract and the role of contracts, assertions,
preconditions and postconditions in testing code
. distinguish between TDD and DbC
. describe situations where TDD and DbC respectively may not apply
. describe the basic types and stages of testing
. understand the role of black-box and white-box testing in the development
of object-oriented systems
. appreciate the limitations of black-box and white-box testing

154
5 Summary

. develop a simple test strategy for both black-box and white-box testing
. explain how software complexity can be measured in object-oriented
systems.

155
Unit 11 Product quality: verification, metrics and testing

References
Beck, K. (2002) Test Driven Development: By Example, Boston, Addison
Wesley Longman Publishing Company Inc.
Beizer, B. (1996) Software System Testing and Quality Assurance, New York,
Van Nostrand Reinhold.
Chidamber, S. R. and Kemerer, C. F. (1994) ‘A metrics suite for object-
oriented design’, IEEE Transactions on Software Engineering, vol. 20, no. 6,
pp. 476–493.
Curtis, B., Sheppard, S. B. and Milliman, P. (1979) ‘Third time charm:
stronger predications of programmer performance by software complexity
metrics’, IEEE Proceedings of the Fourth International Conference on
Software Engineering. Piscataway, NJ, IEEE Press, pp. 356–360.
Gilb, T. (1988) Principles of Software Engineering Management, Wokingham,
Addison Wesley.
Littlewood, B. (1992) ‘Software reliability modelling’ in McDermid, J. A.
(ed) Software Engineers Reference Book, Oxford, Butterworth Heinemann.
McCabe, T. J. (1976) ‘A complexity measure’, IEEE Transactions of
Software Engineering, vol. 2, no. 4, pp. 308–320.
McCall, J. A. and Cavano, J. P. (1978) ‘A framework for the measurement of
software quality’, Proceedings of ACM Software Quality Assurance
Workshop, November 1978, ACM. Published in Cavano, J. P. and McCall, J.
A. (1978) ‘A framework for the measurement of software quality’,
Performance evaluation review, vol. 7, no. 3–4, pp. 133–139.

156
Unit 12 The case study: part 3
Contents
1 Introduction 161
2 Architectural views of the hotel system 162
2.1 The logical view 162
2.2 The process view 166
2.3 The deployment view 167
2.4 Summary of section 168
3 Implementing a first iteration 169
3.1 Choosing an architecture 169
3.2 Implementing the first iteration 171
3.3 Implementing and testing operations 179
3.4 Summary of section 184
4 Using the Facade pattern 186
4.1 Summary of section 188
5 Verifying and validating the implementation 189
5.1 Functionality 189
5.2 Quality attributes 192
5.3 Summary of section 195
6 The way forward 197
6.1 Persistence 198
6.2 Distribution – JavaServer Faces (JSF) 200
6.3 Summary of section 203
7 Summary 205
References 206
1 Introduction

Introduction 1
In this final case study unit we will take the design model of the hotel system
developed in Block 2 Unit 8 and investigate its implementation.
This represents only a first iteration of the system. You can think of it just as
the tip of the twin peaks model we introduced in Unit 9. Following this
model, we will develop requirements and architecture in parallel, giving them
equal importance. At this stage both have very far to go, but they will evolve
together and what we discover in this iteration will feed forward into the next
and allow us to progress in an agile manner.
We will begin by looking at three views of the system: Figure 1 A façade
. the logical view, which describes the system’s main functional elements
and their interactions; broadly, the services the system provides to users.
. the process view, which describes independently executing processes that
will exist at run-time and the communication between them
. the deployment view, which describes how the system will be deployed to
an operating environment of physical computers and networks.
We will use some of the models developed in the earlier case study units and
add some new ones.
The logical view is the one we are most concerned with in TM354, so we
then go on to explore the logical view in enough detail to develop a simple
prototype implementation. For simplicity this first iteration ignores issues of
persistence and distribution, and focuses on how to implement a small set of
use cases and how they can fit with a user interface. The prototype makes use
of several design patterns, and we will discuss these and the contribution they
make to the implementation.
We will then look at how to verify and validate the implementation. We first
describe some system and acceptance tests that could be used for functional
requirements. Then we consider quality attributes, looking at a quality
attribute scenario for usability, and considering what tactics could be applied
to achieve flexibility.
Although this first iteration is very limited it is enough to help confirm some
of the functional and non-functional requirements and to suggest changes that
should be made for the next iteration. We end the unit by exploring some of
these: identifying issues that should be addressed in the next iteration and
outlining what form the solutions are likely to take.

161
Unit 12 The case study: part 3

2 Architectural views of the hotel system


You learnt in Unit 9 that each architectural view has its own viewpoint – the
particular set of conventions and models used to describe it. In this section
we look at what this could mean in practice, giving examples of typical
artefacts from each viewpoint. The main focus of TM354 is the logical view
but we will also look at the other two views.

2.1 The logical view


The logical view describes the functional elements, how they interact, and
how the system can be partitioned into units that can be allocated to teams of
developers.
Typical artefacts are:
. structural models (class and object diagrams), which describe functional
elements
. behavioural models (interaction diagrams), which describe interactions
. package diagrams, which describe a partition of the system into loosely
coupled chunks that can be developed relatively independently.
Figure 2 shows the class diagram that was developed in Block 2 Unit 8.
Block 2 Unit 8 focused on four closely related use cases:
. make reservation
. cancel reservation
. check in guest
. check out guest.
The one studied in the most detail was make reservation. We analysed it
using object diagrams to illustrate the state of the system at various points,
and developed a design for the interaction involved in this use case, as shown
in Figure 3.
In Figure 3 we haven't shown the arguments of the operations because they
make the diagram rather cluttered and our aim at this stage is to capture the
message flow between objects. We will look at the arguments when we come
to implement the use case.
Another type of model that belongs in the logical viewpoint is the package
diagram, which you met in Block 2 Unit 7. This is useful for, among other
things, describing how the system can be decomposed into logical units that
can be allocated to teams of developers. We will assume that the structural
model shown in Figure 2 is simple enough for its classes to form a single
such unit, which we will assign to a package Model (Figure 4).

162
2 Architectural views of the hotel system

HotelChain

ReserverPayer makeReservation()
creditCardDetails : CreditCard cancelReservation()
id : Identity id checkInGuest()
0..1 1
create() checkOutGuest()
canMakeReservation()
number
canCancelReservation()
1
createReserverPayer()
canCheckInGuest()
canCheckOutGuest()
name
1

0..1
Guest
Hotel name : Name
name : Name addressDetails : Address
createReservation() create()
cancelReservation()
checkInGuest() * occupant
checkOutGuest()
available()
HowMany
number : Integer number 1 number
1 1

1..*
RoomType
0..1
* kind : RoomKind 1
Reservation 0..1 cost : Money 0..1
0..1 occupied
reservationDate : Date Room
startDate : Date *
endDate : Date number : Integer
number : Integer *
* * createGuest()
create()

Figure 2 Class diagram with operations

In Block 2 Unit 7 you saw that a UML package can be used to group any
kind of model element, not just classes. Figure 5 shows a package grouping
the use cases we are focusing on in this unit. As we develop the prototype
we will add further model elements and packages to our logical view.

163
Unit 12 The case study: part 3

2: createReserverPayer(...)

makeReservation(...) 2.1: create(...) rp1 : ReserverPayer {new}


: HotelChain
{new}
{new}
1: available(...)

3: createReservation(...)

3.1: create(...)
hotel : Hotel : Reservation {new} : Room
{new} {new}

{new}
roomType : RoomType

Figure 3 Interaction diagram for make reservation

Model HotelChain

ReserverPayer makeReservation()
creditCardDetails : CreditCard cancelReservation()
id : Identity id checkInGuest()
0..1 1
create() checkOutGuest()
canMakeReservation()
number
canCancelReservation()
1 createReserverPayer()
canCheckInGuest()
canCheckOutGuest()
name
1

0..1
Guest
Hotel name : Name
name : Name addressDetails : Address
createReservation() create()
cancelReservation()
checkInGuest() * occupant
checkOutGuest()
available()
HowMany
number : Integer number 1 number
1 1

1..*
RoomType
0..1
* kind : RoomKind 1
Reservation 0..1 cost : Money 0..1
0..1 occupied
reservationDate : Date Room
startDate : Date *
endDate : Date number : Integer
number : Integer *
* * createGuest()
create()

Figure 4 Package Model

164
2 Architectural views of the hotel system

manager reservations

make reservations

cancel reservations

Reserver Receptionist
manager check in and check out

check in guest

check out guest

Figure 5 Use cases grouped into packages

165
Unit 12 The case study: part 3

Receptionist Hotel System

[guest has
no reservation]

[guest has reservation]

enter reservation
reference number
A

look up
reservation
confirm guest s
details

request allocate
room room

open new bill


for the guest

issue key prepare for


to guest next entry

Figure 6 Activity diagram for check in guest

2.2 The process view


The process view is concerned with the separate processes that execute when
the system is running and how they communicate and synchronise their
actions. The issues involved are mostly beyond the scope of this module;
specific notations exist to specify processes and their interactions that we
have not discussed in TM354. We can give a simple illustration of the
behaviour of processes using an activity diagram, one of the model types
used in the process viewpoint.

166
2 Architectural views of the hotel system

Figure 6 is an activity diagram in which a receptionist interacts with the hotel


system to fulfil the check in guest use case.
There are two concurrent flows, the receptionist and the hotel system.
Initially they are unsynchronised but at the join A they synchronise with one
another. Between the join and the fork B their actions are synchronised as
they exchange information. After the fork they cease to be synchronised and
follow independent paths again.
When this process is translated into practice it is very likely that the
receptionist will be working at a different computer from the one where the
hotel system is running, and the two computers will communicate over a
network. The hotel system will be a program, running on one computer that
will be contacted by a program running on a different one. The two programs
will be independent but must synchronise to carry out the use case. The
picture will be essentially the same as Figure 6 but with the workflows now
being separately executing programs.

2.3 The deployment view


The deployment view is concerned with how the system will be deployed to
an operating environment. We can describe deployment with a deployment
model, using a UML diagram which depicts how the high-level components
of the system are deployed to physical computers and how the components
communicate via networks. Figure 7 shows a possible deployment for the
hotel system.

application server

browser EJB container


client «http»

Hotel chain

browser «http» database server


client
persistence «JDBC»
Hotel DB

application
client «RMI-IIOP»
Web container

application «RMI-IIOP» presentation


client

Figure 7 Deployment diagram


This is based on Java EE as described in Unit 10. The links are given
stereotypes that describe what protocol they use. RMI-IIOP and JDBC are

167
Unit 12 The case study: part 3

proprietary protocols for communication with Java EE components and


database management systems respectively.

2.4 Summary of section


This section introduced a subset of the hotel system consisting of the four use
cases that Block 2 Unit 8 focused on.
You saw three architectural views of the system:
. the logical view, which describes the main functional elements
. the process view, which describes the executing processes that will exist
at run-time
. the deployment view, which describes how the system will be deployed to
physical computers and networks.
For each view some typical artefacts were discussed. Artefacts found in the
logical view include class and object diagrams, interaction diagrams and
package diagrams. An example of an artefact from the process view is an
activity diagram. A typical artefact from the deployment view is a
deployment diagram.

168
3 Implementing a first iteration

Implementing a first iteration 3


In this section we will discuss the implementation of a first iteration,
beginning with the architecture.

3.1 Choosing an architecture


As we indicated in the previous section, the aim of this first iteration is to
implement the four use cases.
. make reservation
. cancel reservation
. check in guest
. check out guest.
However we want to be able to run and test these, which cannot be done in
total isolation, because we need at least a subset of the core classes shown in
Figure 2 before we can have a working system of any kind.
In addition we need some way of interacting with the user, so we will require
a user interface. Figure 7 in subsection 2.3 shows clients communicating with
a server over the internet but we will run the prototype all on a single
computer. We will still use a client–server style but communication will
consist of ordinary Java method calls, rather than HTTP requests or RMI-
IIOP.
This gives us a two-layered structure – a core system and a user interface.
We are using the MVC pattern introduced in Unit 9, with the user interface
combining the view and the controller, and the core system being the model.
We want the user interface to be as independent as possible from the model,
so we will use a design pattern that we haven't discussed yet, the Facade
pattern (Gamma et al., 1995).
In built architecture a façade is a frontage for a structure, the face it presents
to the world. The software design pattern gets its name from the same idea. If
a client needs to deal with a number of different objects, they can all be
hidden behind a Facade object, which is like a sort of one-stop shop. This
makes things simpler for the client, because it only needs to know about the
Facade, which presents it with a single unified interface.
The Facade accepts client requests and forwards them to the appropriate
supplier objects. This reduces coupling, because the client doesn’t need to
know anything about the supplier objects, and their implementation can
therefore be changed without any impact on the client. Moreover the Facade
can translate between different data formats, allowing the client to work with
a different set from the supplier objects. This also acts to reduce coupling and
helps limit the scope for errors ‘leaking’ into the model and compromising its
integrity.

169
Unit 12 The case study: part 3

Putting this together, we arrive at the logical view described by Figure 8.


Remember that these are UML packages, which group model elements of any
kind we please, and don't have to correspond to Java packages, although they
may in some cases.

model

«access»

«access»

user
interface

Reserver Receptionist

manage manage checkin


reservations and checkout

Figure 8 Logical view

The process and deployment views are extremely simple for this iteration.
There will be only a single process and there are no issues of synchronisation
since we are using a call-return style in which an object that calls an
Java VM operation on another object must wait for the operation to complete before it
can carry out any further actions. Deployment is trivial in this iteration since
model everything takes place on the same computer and within the same Java
Virtual Machine; see Figure 9.
This diagram shows only the high-level components. The model will actually
Fa consist of a number of subcomponents. We will have more to say about this
when we discuss the Facade pattern again in detail in Section 4.

SAQ 1
user interface
List the architectural styles and design patterns mentioned in the
implementation described above.
Figure 9 Deployment diagram
for this iteration

170
3 Implementing a first iteration

Answer

Call-return, layered, MVC and Facade.

3.2 Implementing the first iteration


Before we can implement any use cases we need a first iteration of the
structural model, so we will begin by considering how we will construct it.
We will make the simplifying assumption that all the classes shown there will
correspond to classes in the implementation model. This is not always the
case in practice, but it seems a reasonable starting point in this case, for the
first iteration at least.
In principle any object-oriented programming language could be used but the
target language we have chosen is Java, so the classes will be Java classes.
The operations will translate into Java methods and constructors, which will
be discussed in subsection 3.3. In this subsection we are concerned with the
following questions.
. How will attributes be realised?
. How will data types be realised?
. How will associations be realised?
We will answer these in turn. In each case we give a broad overview of the
main ideas, then provide examples from the hotel system classes.

Attributes
Attributes represent properties of an object and the values of an object's
attributes represent its state. They are implemented by variables which hold
the attribute values. If the value of an attribute is the same for all instances
of a class it can be implemented as a class variable. Otherwise it will be
implemented as an instance variable and each instance of the class will have
its own value for the variable.
Attributes can also have visibility. In Java, class and instance variables can be
given one of four levels of visibility:
. private – visible only to the class and objects of the same class
. package – visible to classes and objects in the same Java package
. protected – visible to classes and objects in the same Java package and to
the class's subclasses and their instances anywhere
. public – visible to all classes and their instances.
For private, protected and public visibility, Java provides explicit modifiers. If
no visibility is specified it defaults to package access.
Variables may additionally be declared final. Once a value has been assigned
to a final variable no other value can subsequently be assigned to it. If the

171
Unit 12 The case study: part 3

value is an object it may still be possible to modify that object, depending on


its class, but we cannot put a different object in its place.
A class's variables are part of its implementation and should generally be
hidden from other classes by making them private. Objects of other classes
that need to access an object's attributes will do so via the appropriate
operations in the object's interface.
Here are two examples of how attributes of classes in Figure 2 can be
realised as Java variables.
1 In Reservation the start date is represented by a variable of type Date
with package visibility.
Date startDate;

2 In Room the room number is represented by a variable of type int, again


with package visibility.
int number;

The choice of package visibility is reasonable: the attributes will be visible to


classes within the core system package but hidden from classes in other
components, such as the user interface.
As you can see, although Figure 2 gives the type of number as Integer we are
not therefore obliged to use the Java class Integer. When it comes to
implementation we are responsible for choosing a suitable type and in the
present case the primitive data type int is adequate for our purposes.

Data types
The attributes in Figure 2 all have data types such as Name, Integer,
RoomKind, Money and Date. High-level languages come with a range of
standard data types built-in and in many cases one of these will be suitable to
represent an attribute, as you saw in the two examples above.
But others, such as RoomKind and Money, cannot sensibly be represented by
any existing Java types. For these, user-defined types will be required. We
will look first at RoomKind.
We have not previously considered what the possible room types are but let's
assume that the range of possibilities is single, double, twin, familyOf3,
familyOf4 and familyOf5. A list of constants such as this can be represented
in Java as an enumerated type, called an enum. RoomKind can be declared as
an enum like this.
enum RoomKind {
SINGLE, DOUBLE, TWIN, FAMILYOF3, FAMILYOF4, FAMILYOF5
}

RoomKind can then be used to declare variables in the same way as any
other type, for example

172
3 Implementing a first iteration

private RoomKind roomKind;

However the only values that can then be assigned to the variable roomKind
are the constants defined by the enum, for instance
roomKind = RoomKind.TWIN;

This is exactly what we want because a room type must always be one of the
predetermined possibilities listed in the enum.
Now let's consider Money. We mentioned analysis patterns when discussing
reuse in Unit 9 but without giving any examples.
Representing a sum of money takes more than just a numerical value,
because it will also involve a currency. Fowler (1997) discusses an analysis
pattern called Quantity, which provides useful guidance on how to represent
quantities such as amounts of money. Here is the pattern.

Quantity pattern
Name. Quantity.
Purpose. Represents dimensioned quantities, for example 6 ms, 30 kg.
How it works. The key idea is to define a Quantity class that combines the
amount with the unit.
When to use it. When you need to represent a quantity with both a value and
a unit.
Example of use. Height in metres, mass in kilograms.

Exercise 1 Money
– amount : Integer
Explain how the Quantity pattern can be applied to the user-defined type – currency : Currency
Money.
Solution
Figure 10 User-defined type
Money is a quantity with a value (the amount) and a unit (the currency). Money

Money can be represented as shown in Figure 10, where we have assumed


that both attributes are private.
To implement this in Java we will use the classes BigDecimal to represent BigDecimal is a Java class
the amount and String to represent the currency. BigDecimal allows us to suitable for representing decimal
do calculations with the accuracy required in financial applications. Although values where the result of
calculations must be exact.
Java has a built-in class Currency, for our purposes it is enough to represent
the currency using the standard three-letter abbreviations, for example USD,
GBP, EUR.
Below is a skeleton Java implementation of a Money class with these Three-character currency codes
are defined in standard ISO 4217.
attributes.
public class Money {
private final BigDecimal amount;
private final String currency;

173
Unit 12 The case study: part 3

public Money(
BigDecimal anAmount, String aCurrency) {
amount = anAmount;
currency = aCurrency;
}
public BigDecimal getAmount() {
return amount;
}
public String getCurrency() {
return currency;
}
}

The attributes are private because other classes, even in the same package,
should not be able to access them directly. The amount and currency are
declared final because Money instances should be immutable: the values of
their attributes should be set when they are created and not altered thereafter.
If we need to represent a different sum of money we create a new instance
rather than modifying an existing one.
We can also associate operations with the Money type, for example:
context Money::plus(someMoney : Money) : Money
pre:
- - the currency of someMoney and this Money object are the
same
post:
- - the result of adding someMoney to this Money object will
be
- - a Money object whose currency is the same as this Money
object’s currency
- - and whose amount is the sum of this Money object’s
amount and someMoney’s amount

SAQ 2

Although we will not be following strict test-driven development we aim


to draw up black-box tests for an operation before actually writing any
code. Describe a test case you could use for the operation Money::plus.
You don't have to write the test, just outline what it would be.
Answer

Because of the precondition there is no need to test cases in which the


currencies are different.
A simple test would consist of taking two Money objects with the same
currency, applying the plus operation, and checking that the postcondition

174
3 Implementing a first iteration

is met – that is, a Money object is returned whose currency is the same
and whose amount is the sum of this Money's amount and someMoney's
amount.

If the test in the solution to SAQ 2 above is implemented in Java we get


something like the following.
// set up objects – the 'fixture'
BigDecimal amount1 = new BigDecimal("12.45");
BigDecimal amount2 = new BigDecimal("67.89");
Money money1 = new Money(amount1, "EUR");
Money money2 = new Money(amount2, "EUR");
// invoke method under test
Money sum = money1.plus(money2);
// assert the postcondition holds
assert sum.getCurrency().equals(
money1.getCurrency());
assert sum.getAmount().equals(
money1.getAmount().add(money2.getAmount()));

We first set up the objects needed for the test – this is called the fixture.
Then we invoke the method being tested, using the objects of the fixture.
Finally we use the assert keyword to test that the postcondition holds. It is
split into two assertions because if it fails we want to know which part was
responsible.
The test can be run using the JUnit testing framework mentioned in Unit 11.
We don't have space for a discussion of JUnit but in fact making the code
above into a JUnit test is very straightforward. All we have to do is make it
the body of a method annotated with the class org.junit.Test from the
JUnit framework.
@org.junit.Test
public void testPlus() {

Then we can run the class containing the method and the JUnit framework
will report whether the test was passed.
The implementation of the plus operation is as follows.
/**
* Returns a Money object whose currency is the same as
* this object's currency and whose amount is the sum
* of this object's amount and someMoney's amount.
*/
public Money plus(Money someMoney) {
return new Money
(this.amount.add(someMoney.amount), this.currency);
}

175
Unit 12 The case study: part 3

Associations
Associations are more complex than attributes for several reasons:
1 They involve two classes rather than just one.
2 They have multiplicities. A multiplicity greater than one means a link to a
collection of objects rather than just a single object.
3 They may be qualified. If an association is qualified then every link of the
association has a unique identifier.
In our implementation, associations will be implemented by the object at one
end holding a reference to the object or objects at the other end. This is not
the only way associations can be implemented but it is the only approach we
have room to explore.
We start with the simplest case.

Association end with multiplicity one


Consider the association from Room to RoomType shown in Figure 11. This
association tells us that each Room object must be linked to a RoomType
object – that is, the room type of that room.

Room RoomType
number kind
1..* 1 cost

Figure 11 Association end with multiplicity one

This link will be represented by using an instance variable in the Room class
to store a reference to the RoomType instance concerned. This is shown
schematically in Figure 12.

Room instance RoomType instance


roomType

Figure 12 Object reference representing association link

The code in Room to declare a suitable variable would look like this.
private RoomType roomType;

The code to create the link would look like the following, assuming that
roomType holds a reference to a RoomType instance.

176
3 Implementing a first iteration

this.roomType = roomType;

Again the variable that holds the link is part of the implementation, so it is
hidden from objects of other classes by being declared private.

Association end with multiplicity many


SAQ 3

Consider the association from Hotel to RoomType in Figure 13. From the
multiplicities what do you know about how the association needs to be
implemented at the Hotel end?

Hotel 1..* RoomType


1
name kind
cost

Figure 13 Association end with multiplicity many

Answer

The multiplicity at the RoomType end shows that Hotel may be linked to
many RoomType objects. So a Hotel object has to be able to hold
references to multiple RoomType objects.

SAQ 4

What does Figure 14 tell us about how the association from ReserverPayer
to Reservation needs to be implemented at the ReserverPayer end?

ReserverPayer 1 0..1 Reservation


creditCardDetails number reservationDate
id ....

Figure 14 Qualified association

Answer

As the association is a qualified one, with qualifier number, the multiplicity


tells us that each ReserverPayer object needs to be able to hold a reference
to at most one Reservation object for each value of number.

We look first at the case where the multiplicity is many but the association is
not qualified, for example the association from Hotel to RoomType in
Figure 13. This link will be represented by using an instance variable in the
Hotel class to store a reference to a collection of RoomType references,

177
Unit 12 The case study: part 3

rather than just a reference to a single instance. This is shown schematically


in Figure 15.

RoomType instance

Hotel instance RoomType instance

roomTypes

RoomType instance

Figure 15 A collection of references

The code in Hotel to declare a suitable variable would look like this.
private Set<RoomType roomTypes;

The collection used in this example is a set, which is an unordered collection


of elements with no duplicates allowed. The type declaration <RoomType>
specifies the type of element the collection is allowed to hold. Using a set for
the collection of room types makes sense because the order does not matter
and no room type should be repeated.
The code to create an additional link, in other words to add another
RoomType reference to the collection, would be as follows, assuming that
roomType holds a reference to a RoomType instance.
roomTypes.add(roomType);

Another type of collection is a list, which does allow duplicates, and whose
elements are in a particular order and can be accessed using an index that
gives their positions in the list.
Next we look at the case where the association is qualified. The example we
take is ReserverPayer to Reservation. Although a ReserverPayer can be
linked to many Reservations, there is at most one Reservation for each
reservation number. To put it differently, for each unique combination of a
ReserverPayer and a reservationNumber there corresponds either one
Reservation or none.
This is shown schematically in Figure 16.

178
3 Implementing a first iteration

Reservation instance

ReserverPayer instance
number Reservation instance

reservations 1
2
3
4
Reservation instance

Figure 16 Qualified association

In Java a qualified association corresponds to a map, which is a collection of


key–value pairs. The keys are unique, although the values may not be. Given
a key, we can look up and retrieve the associated value if there is one. The
same structure is also called a dictionary in some languages.
The code in Hotel to declare a suitable variable would look like this.
private Map<Integer, Reservation> reservations;

This declares that the map keys will be of type Integer and the values of
type Reservation.
The code to add another key–value to the map could be like the following,
assuming aNumber and aReservation already hold references to the
appropriate objects.
reservations.put(aNumber, aReservation);

3.3 Implementing and testing operations


Having considered the implementation of attributes and associations we turn
now to implementing operations.
To get from an operation in the design model to a method that implements
that operation involves the following steps:
1 matching the signature and return type
2 writing tests
3 running the tests, which should fail at that point
4 realising the contract of the operation in code

179
Unit 12 The case study: part 3

5 rerunning the tests and if one or more fail correcting the code until all the
tests are passed.
As you can see, we are following test-driven development and writing the
tests before the code.
We will illustrate this first for the operation createReserverPayer in
HotelChain. Here is its specification.
context HotelChain::createReserverPayer(
id : Id, name : Name, address : Address,
creditCardDetails : CreditCard) : ReserverPayer
pre:
- - none
post:
- - a new ReserverPayer is created and returned
- - its attributes are initialised to the given values
- - the HotelChain is linked to the ReserverPayer

We can translate this contract directly into a JUnit test method. First we set
up the required parameters, including creating any objects that are necessary.
This is the fixture.
String reserverId = "123";
String reserverName = "Harry Hawkins";
String[] reserverAddress =
{"12 High Street", "Little Snoring"};
CreditCard reserverCreditCard = new CreditCard(
reserverName,
reserverAddress,
"1234123412341234".toCharArray());
HotelChain instance = new HotelChain();

Then we invoke the createReserverPayer operation.


ReserverPayer result = instance.createReserverPayer
(reserverId, reserverName, reserverAddress,
reserverCreditCard);

Finally we check that the postconditions hold.


assert result != null;
assert result.getName().equals("Harry Hawkins");
assert result.getAddress()[0].equals(
"12 High Street");
assert result.getAddress()[1].equals(
"Little Snoring");
assert result.getCreditCardDetails().equals(
reserverCreditCard);
assert instance.getReservers().get("123").equals(

180
3 Implementing a first iteration

result);

Having written the test we next write a skeleton version of the method, with
the required signature. Initially the method just returns null, so the code can
compile.
public ReserverPayer createReserverPayer(
String reserverId,
String reserverName,
String reserverAddress,
CreditCard reserverCreditCard) {
return null;
}

We run the test, which should fail because the method does not implement
the postconditions yet.
Once we have confirmed that the test fails with the skeletal implementation
we can write the body of the method to implement the contract of the
operation.
public ReserverPayer createReserverPayer(
String reserverId,
String reserverName,
String reserverAddress,
CreditCard reserverCreditCard) {
// implement contract of operation
ReserverPayer reserverPayer;
reserverPayer = new ReserverPayer(
reserverId, reserverName,
reserverAddress, reserverCreditCard);
reservers.put(reserverId, reserverPayer);
return reserverPayer;
}

We rerun the test and this time it should pass. The code above is correct, so
no assertion errors will be thrown. However in other cases an assertion error
will occur, indicating one or more defects in the code. As soon as an
assertion error is encountered, program execution ceases, which allows us to
locate the point at which a problem has occurred.
Once a fault is located we correct the code and run the test again. If now a
different assertion fails, that shows there is another bug. We fix the new bug
and run the test again, and so on until no assertion errors are thrown and the
method passes the test.
Implementing createReserverPayer depends on the constructor in the class
ReserverPayer, which therefore needs to have been implemented first. It
will generally be the case that operations depend on others, so we will need
to choose an implementation schedule that takes this into account.

181
Unit 12 The case study: part 3

Also, setting the test up required the creation of objects belonging to a


number of other classes. In cases where the required classes are not yet
implemented we may sometimes need to create ‘mock’ objects, which are
partial implementations that provide limited functionality just sufficient for
the purpose of the test.

Exercise 2
Give an outline of what would be needed for a JUnit test of the method
corresponding to the operation createGuest below.
You do not need to write actual code, only to describe what would be needed
for the fixture, what the invocation would involve, and how the result should
be checked.
context Room::createGuest
(name : Name, address : Address) : Guest
pre:
- - none
post:
- - a new Guest object will have been created with
attributes initialised to the given values
- - the new Guest object will have been returned

Solution
For the fixture we would create a Name object aName and an Address object
anAddress.
The invocation would be createGuest(aName, anAddress) and the
return value would be assigned to a variable result of type Guest.
We would check that:
. result is not null
. its name attribute is set to aName
. its address attribute is set to anAddress.

Next we consider a more complex operation, makeReservation, with the


specification below.
context HotelChain::makeReservation(hotelName : Name,
startDate : Date, endDate : Date, roomType : RoomType,
name : Name, address : Address, creditCardDetails :
CreditCard) : String
pre:
- - startDate is before endDate
- - there is a Hotel with Name hotelName, and it has
RoomTypes of roomType
post:

182
3 Implementing a first iteration

- - if available(startDate, endDate, roomType) executed


in the Hotel with hotelName returned true then
-- a new Reservation object, reservation, will have
been created
-- if there is not a ReserverPayer with given name
and address in that Hotel then
-- a new ReserverPayer object will be created
with attributes set to name, address,
creditCardDetails and a newly generated id
-- the HotelChain will be linked to the new
ReserverPayer object
-- that Hotel will be linked to reservation
-- that ReserverPayer will be linked to reservation
-- reservation will be linked to roomType
-- reservation will be linked to the ReserverPayer
-- reservation will be linked to a previously free
Room
-- that Room will be linked to reservation
-- a string indicating the reservation number will
have been returned
- - otherwise, a string indicating failure will have
been returned

Verifying the precondition of this operation is the responsibility of the user


interface, so when writing the tests for this operation we need to consider
only inputs that satisfy the precondition.

Exercise 3
List what other operations makeReservation is directly dependent on, giving
their signatures. You will have to invent suitable names for some of them.
Solution
context Hotel::available(startDate Date, endDate Date,
roomType RoomType) : Boolean
context HotelChain::createReserverPayer(
id : Id, name : Name, address : Address,
creditCardDetails : CreditCard) : ReserverPayer
context Hotel::createReservation(startDate : Date,
endDate : Date, roomType : RoomType, reserverPayer :
ReserverPayer) : Reservation
context Hotel::addReservation(reservation :
Reservation) : void
context ReserverPayer::addReservation(reservation :
Reservation) : void
context Reservation::setRoom(room : Room) : void

183
Unit 12 The case study: part 3

context Room::addReservation(reservation :
Reservation) : void

Exercise 4
List the objects that will need to be created to test makeReservation,
assuming that the ReserverPayer is already known to the Hotel. You are not
expected to write any code or invent any sample attribute values, just to
describe what objects are required and how they are linked. It may be useful
to look again at Figure 2.
Solution
We need:
a HotelChain
a Hotel linked to the HotelChain
a RoomType linked to the Hotel
a Room linked to the RoomType (and the Hotel)
a Date startDate
a Date endDate
a Name
an Address
a ReserverPayer with the given name and address, linked to the HotelChain.
The fixture for a test of makeReservation turns out to be quite elaborate and a
similar collection of objects is likely to be needed in testing other complex
operations. The usual technique in such a case is to write a separate setup()
method to do the work. The JUnit framework will then run this method
automatically before each test method, so that each test is provided with a
fresh copy of the fixture.

3.4 Summary of section


This section considered a first iteration of the system. It began by choosing
an architecture based on the MVC pattern, which separated the core system
from the user interface. To further reduce coupling between the two a Facade
layer was also introduced. The Facade hides all the details of the model from
the user interface. Client requests are sent to the interface, which forwards
them to the appropriate supplier objects in the model, performing any
conversion between data types that may be required.
Based on this architecture we were then able to present a logical view of the
system, and a simplified deployment view sufficient for a first iteration.
The section then went on to explore in some detail how the system can be
implemented in Java, in particular how to realise attributes, associations and
behaviour.

184
3 Implementing a first iteration

You saw that attributes, which represent properties of an object, are


implemented as variables that hold attribute values. These are usually hidden
from objects of other classes and access is provided via appropriate methods.
Attributes have data types and when implemented in Java these may often be
realised as built-in Java types. In some cases, however, we need to define
new user-defined types. Two examples were discussed, an enumeration,
which specified all the possible room types, and a class Money, which
provides a way of representing sums of money in a particular currency, and
performing operations on them, such as addition.
You then saw how associations can be implemented, by using suitable
variables to hold references. Associations ends with multiplicity one can be
represented by a simple variable, but for multiplicity many a collection is
required. Associations may also be qualified and such an association is
realised by a map.
Finally you saw that operations are realised as Java methods. We followed a
test-driven development approach, in which the first step is to write an empty
method with the required signature. Next, tests are written based on the
specification of the operation. The tests should fail at this point, since the
body of the method is missing. Code is written to realise the contract of the
operation and the tests are rerun. If they pass, the method is accepted. If they
still fail, the code is corrected and the test is run once more. This cycle is
repeated until the tests run without error. The section ended by looking at
examples of how such unit tests are carried out in practice.

185
Unit 12 The case study: part 3

4 Using the Facade pattern


In this implementation of the hotel system you have seen a number of
architectural styles and design patterns, including one new structural design
pattern, Facade. You also met the Quantity analysis pattern.
In this section we look further at the Facade pattern and its use in the
prototype.
Here is its description using the short template we introduced in Unit 9.

Facade pattern
. Name. Facade.
. Purpose. Replaces a set of separate interfaces with a single unified
interface which is easier for clients to use and which reduces coupling
between the client and the supplier objects.
. How it works. A Facade object sits between the clients and the individual
interfaces. The Facade receives client requests and translates them into
requests that can be understood by the supplier objects.
. When to use it. When we want to present clients with a single interface
instead of several, or when we want to reduce the coupling between
clients and system objects, for example by allowing clients and supplier
objects to vary their data representation independently.
. Example of use. An online shop may act as a front for a number of
individual suppliers but this is hidden from customers, who interact with a
single ordering and payment system. The shop may accept card payments,
and convert the amount charged from the currency used where the shop is
based into the one used in the customer’s country.
Usually only one instance of the Facade class needs to exist, so we often
make it a Singleton.
Figure 17 shows a Facade hiding details of a core system, including
converting between data types, which we will explain in detail below.

primitive data original


View built-in types data
Object type types

Model

Figure 17 Facade

186
4 Using the Facade pattern

In our system we use a Facade mainly to decouple the user interface from the
business logic. Two main sorts of operation are required:
. operations that implement use cases (such as makeReservation)
. operations that provide the user interface with additional information.
To minimise coupling between the user interface and the business layer we
want the user interface to know as little as possible about the core system. In
particular the two sides should trade in different kinds of data. At the user
interface end the data should be confined to primitive data types, built-in Java
utility types such as String, and objects that the user interface knows only
as instances of Object (in Java): their exact class is not known to the user
interface.
Although it might seem that the methods provided by the Object type are
too limited to be much use, in fact methods such as equals, hashCode and
toString allow objects to be stored in collections, compared with one
another, and displayed in graphical components such as lists.
So the user interface is completely unaware of the actual classes of the
implementation objects in the business layer and interacts with them entirely
through the Facade. Because it does not know the classes concerned it is
decoupled from the details of the implementation, which could change
without the user interface being affected.
However a consequence is that when the user interface passes objects, apart
from utility objects such as strings and dates, into the business layer, their
type must be Object at that point. It is the responsibility of the Facade to
cast them back to their proper types before forwarding them to the business
layer.
So, for example, when a user selects an item from a pick list and it is sent
into the core system the user interface will simply treat it as an instance of
Object, and the Facade will handle its conversion to the type expected by
the business layer. Two-way conversion between
data types can also be achieved
As an example of a Facade operation that provides the user interface with with MVC, or with a multi-tiered
information here is the getHotels method in the Facade class. This method architectural style.
returns an array of the hotels in the hotel chain.
public Object[] getHotels() {
return (Object[]) hotelChain.getHotelsArray();
}

You can see that this returns an array with elements type Object, not Hotel,
for the reasons explained above.
The second example shows how we support use cases. The method
makeReservation of Facade takes parameters supplying the data needed to
carry out the use case.
This method invokes the corresponding method in HotelChain but before
this can happen the Facade method must cast the object representing the
RoomType from Object to RoomType, which is the class expected by the

187
Unit 12 The case study: part 3

method in the business layer. Here is the Facade method. Notice the type of
the argument Object roomType and the explicit cast (RoomType)
roomType further on.
public String makeReservation
(String hotelName, Date startDate,
Date endDate, Object roomType,
String reserverName, String [] reserverAddress,
String reserverCreditCardNumber) {
return hotelChain.makeReservation
(hotelName, startDate, endDate,
(RoomType) roomType,
reserverName, reserverAddress,
reserverCreditCardNumber);
}

4.1 Summary of section


This section discussed the Facade design pattern and how it is used in the
hotel system. You saw that to reduce coupling to a minimum the user
interface should be unaware of the actual classes of objects in the business
layer. To maintain the separation, the user and the interface and the core
system should trade in different types of data and the Facade is responsible
for the conversions between types that this makes necessary.
You learnt that two kinds of operation are needed: ones that implement use
cases, and ones that provide the user interface with additional information,
and you saw an example of each kind.

188
5 Verifying and validating the implementation

Verifying and validating the implementation 5


5.1 Functionality
In Section 3 we sketched how unit testing would be carried out, taking a test-
first approach and using a testing framework such as JUnit. In this section we
look at how the overall system would be tested.

System testing
System testing is testing we carry out to check that the completed system
meets its requirements. The requirements are structured as specifications of
use cases, so it makes sense to take these as the basis of our system testing.
We will deal only with functional requirements here: non-functional
requirements will be considered separately in subsection 5.2.
Because we are testing the complete system we must look not only at
individual use cases but also at extended scenarios involving several use
cases. Error conditions can frequently cut across more than one use case.
As an example we might consider the scenario where a customer of the hotel
chain makes a reservation and then cancels it. First we would test that a
reservation can be made correctly. Once those tests have been passed we
would test the cancellation operation.
Because we have limited space we will focus the discussion mainly on testing
the make reservation use case, followed by some scenarios across the two use
cases make reservation and cancel reservation.
System testing is designed to see if the system that has been built meets its
specification. The specifications of the two use cases we shall be
investigating are given in Tables 1 and 2.

Table 1 A textual description of the make reservation use case

Identifier and UC1 make reservation


name
Initiator Reserver or Receptionist
Goal A room in a hotel is reserved for a guest.
Precondition None (that is, there are no conditions to be satisfied before
carrying out this use case).
Postcondition A room of the desired type will have been reserved for the
guest for the requested period, and the room will no longer be
free for that period.
Assumptions The expected initiator is a reserver or receptionist, using a
web browser to perform the use case. The guest is not already
known to the hotel’s software system (see step 5). The
receptionist (if an actor) communicates details to the client
when making a booking.
Main success scenario
1 The reserver/receptionist makes a reservation request.

189
Unit 12 The case study: part 3

2 The reserver/receptionist selects the desired hotel, dates and type of


room.
3 The hotel system provides the availability and price for the request;
that is, one or more offers are made.
4 The reserver/receptionist accepts the offer.
5 The hotel system requests identification and contact details.
6 The reserver/receptionist provides identification and contact details of
the guest for the hotel system’s records, as well as credit card details.
7 The hotel system creates a reservation and gives it a number.
8 The hotel system allocates a room to the reservation.
9 The hotel system reveals the reservation number to the reserver/
receptionist.
Extensions
3.a.1 No availability in this hotel. The hotel system offers alternatives at a
different hotel or hotels within a limited radius.
3.b.1 No availability in this hotel or in any other hotels within a limited
radius. The hotel system informs the reserver and terminates the use
case.
4.a.1 Offer not accepted. The hotel system terminates the use case.
6.a.1 Guest already on record. The guest is identified and (if needed) their
record is updated and the scenario continues at step 7.

Table 2 A textual description of the cancel reservation use case

Identifier and name UC2 cancel reservation


Initiator Receptionist or Reserver
Goal A guest’s reservation of a room at a hotel is cancelled.
Precondition There is an active reservation (in the future and not
cancelled) for a room at a hotel for a guest with a
reservation number.
Postcondition The reservation will have been cancelled, the allocated
room will be freed.
Assumptions The expected initiator is a receptionist or reserver.
Main success scenario
1 The receptionist/reserver requests the cancellation of a reservation.
2 The receptionist/reserver enters the reservation number, or the dates,
name and address.
3 The hotel system retrieves the corresponding reservation.
4 The hotel system frees the allocated room.
5 The receptionist/reserver confirms the cancellation.
6 The reservation is cancelled, and the hotel system informs the
receptionist/reserver of the cancellation.

Tests for make reservation


For each scenario implied by this use case we need a test to ensure that the
corresponding fit criterion is met. Since we are dealing with functional

190
5 Verifying and validating the implementation

requirements the fit criterion is simply whether the requirement in question


has been fulfilled.
The make reservation use case has four extension points, and so a reasonable
set of tests will test the main scenario and the alternatives presented by each
exception. More thorough testing might look at each permutation across the
exceptions, and would be appropriate, for example, in the case of a real-time
system.
We need the tests shown in Table 3.

Table 3 System tests for make reservation

Test no. Scenario


1 Main success scenario
2 No availability in the hotel
3 No availability in other hotels
4 Guest already recorded

Remember that, as we carry out the above tests, we will need to have
selected data that shows us that the fit criteria for the detailed software
requirements given in Block 1 Unit 4 are satisfied.

Tests across make reservation and cancel reservation


The cancel reservation use case has one main scenario and no extensions.
However we can consider the case where an attempt is made to cancel a non-
existent reservation (that is, with a reservation number that is not currently
allocated) as a separate scenario. An exhaustive approach to testing would
combine these two scenarios in all possible combinations with the four
scenarios given for the make reservation use case. However we will simply
use the make reservation use case to set us up for testing the two scenarios of
cancel reservation.
We need the tests shown in Table 4. Here we are doing system
testing, which takes as its starting
Table 4 System tests across make reservation and cancel reservation point the original requirements,
as opposed to integration testing
Test no. Scenario which is more concerned with
verifying that classes interface
1 Make a reservation and then cancel it together correctly.
2 Cancel a non-existent reservation

Acceptance testing
Acceptance testing is similar to system testing but carried out by the
customer to decide whether to accept or reject the software.
One way to design acceptance tests is to base them on user stories, which
were touched on in Block 1 Unit 2. A user story describes a piece of
functionality from the viewpoint of a user who will benefit from it and says
what the user’s expectations will be, in a single paragraph. Block 1 Unit 2

191
Unit 12 The case study: part 3

gave as an example from an airline application: ‘As a customer I can see


days, times and cost of flights to the destination.’

Exercise 5
User stories have not been dealt with in detail but you saw some examples in
Block 1 Unit 2. In this exercise we ask you to have a go at writing user
stories corresponding to tests 1–4 in Table 3 above, recognising that writing
user stories is a difficult art and you will not be an expert.
We are asking only for the story itself: you do not need to provide a priority,
an estimate or constraints.
Should any of your stories be factored into smaller ones?
Solution
These are the stories we came up with. Yours will probably be different. The
user in any of these could be the customer or the receptionist.
. As a customer I want to make a reservation so that I can stay at the hotel.
. As a customer I want to be offered an alternative hotel if my first choice
is not available.
. As a receptionist I want the system to inform me if the reservation a
customer requests cannot be made in any of our hotels.
. As a receptionist I want the system to inform me if a customer’s details
are already known to it.

5.2 Quality attributes


Unit 10 discussed quality attributes and their impact on architectural design,
based on the ideas presented in Bass et al. (2012). You were introduced to a
six-part model for documenting quality attribute requirements and measuring
whether they have been met. You were also introduced to tactics for quality
attributes: reusable solutions for meeting quality attribute requirements.
These ideas can be applied in some form to almost any quality attribute but
in Unit 10 we only had space to look in detail at scenarios for two quality
attributes, performance and usability, and at two sets of tactics, those for
performance and flexibility.
In this subsection we give an example of a quality attribute scenario and a set
of tactics that can be applied to our hotel system, building on the examples
given in Unit 10. Because our first iteration is deliberately much simpler than
the system we expect to arrive at eventually, looking at performance doesn’t
seem valid at this stage. So we shall look at the other quality attributes dealt
with in Unit 10, first developing and applying a quality attribute scenario for
usability, and then going on to consider briefly whether the architecture we
have so far uses the right tactics if we want to achieve flexibility.

192
5 Verifying and validating the implementation

Usability and the user interface


Figure 7 from Unit 10, quality attribute scenarios based on the six-part mode
of the Bass et al six-part model, is repeated here as Figure 18.

environment

artefact
stimulus response
source measure

Figure 18 Quality attribute scenario

Unit 10 looked at how this model can be applied to usability. Tables 5 and 6,
which are reproduced here for convenience, show the generic types of value
for a usability scenario, and then give an example of how these could map to
specific values.

Table 5 Types of value for usability scenarios

Part of scenario Types of value


Source end users
Stimulus user wants to:
– learn system features
– use system efficiently
– minimise impact of errors
– feel comfortable
Artefact system
Environment setting preferences
using system
Response one or more of:
– supporting learning
– supporting efficient use
– minimising impact of errors
– allowing adaptation
– making user comfortable
Measure task time
number of errors
number of problems solved
user satisfaction
success rate
time or data lost

Source: adapted from Bass et al., 2012

193
Unit 12 The case study: part 3

Table 6 Example usability scenario

Part of Type of value Actual value


scenario
Source end user experienced operator
Stimulus user wants to: use messaging feature efficiently
– use system
efficiently
Artefact system central system
Environment using system normal system use
Response supporting efficient system allows messages to be reused by
use copy and paste
Measure task time time saved by copy and paste at least
20 seconds on average

In the next exercise we ask you to try writing and applying a usability
scenario for the hotel system implementation.

Exercise 6
Write a single specific usability scenario for the hotel system, taking the
example in Table 6 as your starting point. There are no absolute rights and
wrongs here: concentrate on producing a scenario that you feel is meaningful
and realistic and choosing a sensible measure for it.
When you have written your scenario run the hotel system and play the
scenario out, imagining yourself as much as you can in the role of the end
user, and test whether the measure is met.
Solution
We came up with Table 7 but of course your solution may be very different.

Table 7 Solution to Exercise 7

Part of Type of value Actual value


scenario
Source end user novice operator
Stimulus user wants to: learn use of make reservation feature
– learn to use system without special training
quickly
Artefact system hotel system
Environment using system normal system use
Response supporting efficient use system is sufficiently self-explanatory
for a novice to pick it up quickly
without additional instructions.
Measure task time a novice can successfully make a
reservation within 2 minutes.

194
5 Verifying and validating the implementation

We played our effort out and considered that an inexperienced user would
still be able make a reservation within two minutes.

Tactics for flexibility


The main tactics for architecture flexibility identified in Unit 10 were:
. minimise coupling
. maximise cohesion
. keep modules small
. bind as late as possible.

Exercise 7
Discuss how flexible the architecture used in the first iteration of the hotel
system is. Which of the tactics above does it follow and in what ways?
Which tactics are not followed? You may want to look again at the package
diagram in Figure 7 above, and also quickly revisit subsection 4.4 of Unit 10.
Solution
These are our comments. You may have thought of different ones.
The core system has been separated from the presentation and an
intermediate layer, the Facade, has been introduced. All the detail of the
supplier objects is hidden behind this Facade, and even the data types have
been decoupled. So coupling between the presentation and the model is
minimal.
Figure 5 shows that we have grouped the use cases covered by the first
iteration into two packages, and if the modules of the core system reflect this
division they should be comparatively small, and should have good cohesion.
Using the Facade has lowered cohesion though, because we have created an
interface that cuts across both modules and provides operations that are less
closely related.
Late binding is harder to see, but in fact the Facade delivers this, because the
supplier object that actually deals with a request can be changed behind the
scenes, so that the request is associated with a different supplier.

5.3 Summary of section


This section was concerned with whether the system meets its requirements.
First you looked at system testing, which is testing of the completed system
based on use cases. Because this is testing the complete system, scenarios are
needed that extend across several use cases. As an example you saw how we
could specify tests across make reservation and cancel reservation, and
carried these tests out as a practical activity.

195
Unit 12 The case study: part 3

You then looked at acceptance testing, which is similar to system testing but
is carried out by the customer, and learnt how acceptance tests can be based
on user stories. You then carried out an example acceptance test.
The tests so far were concerned with functional requirements but quality
requirements are of equal importance. A quality attribute scenario for
usability was developed using the six-part model of Bass et al. introduced in
Unit 10, and you then investigated whether the system that has been
implemented met the associated measure.
Tactics are reusable solutions for meeting quality attribute requirements. The
section ended by exploring how far the architecture chosen for the first
iteration supports the quality attribute of flexibility.

196
6 The way forward

The way forward 6


In this section we will briefly touch on some of the issues that will need to
be covered in future iterations. We begin by identifying some of the ways in
which the first iteration is incomplete, then go on to sketch how two
particularly important areas could be addressed. These are intended only as
tasters – we don’t have space to go into details – but we hope these brief
introductions give something of the flavour of these important technologies.

SAQ 5

The existing system is incomplete in a number of ways and also has a


number of issues that need addressing. Identify some of these areas.
Answer

Here are some points we came up with, not arranged in any special order.
You might have thought of others.
. The system is not persistent.
. The system is not distributed.
. It may be awkward to adapt.
. The functionality is not complete, even for the increment we have
chosen.
. Some refactoring may be needed.
. The Facade is a big interface that might pose a problem when we
come to make the application distributed.
. The system does not cover all the use cases given in the
specification.
. The user interface is a Java program but potential guests will expect
to make reservations via web pages.

SAQ 6

One of the possible issues with the current implementation is that the
Facade class presents a very large and complex interface that lacks
coherence and may be difficult to use. Suggest two ways in which this
problem could be addressed.
Answer

We thought of two possible approaches:


. Have several smaller Facades, each dealing with a particular aspect.
Then each interface should be more coherent and small enough to
understand easily.
. Have the clients communicate via a RESTful web service, as
discussed in Section 5 of Unit 10. This would keep the interface
very small and allow clients to invoke the service without needing to

197
Unit 12 The case study: part 3

know so much detail, since the response to one call could suggest
how to make the next.

The two areas we will explore are how the application could be made
persistent, and how it could be made distributed and accessible from a web
browser.

6.1 Persistence
As we have noted in the answer to SAQ 5, this first iteration has no
provision for persistence. If we close our program down, or it crashes, then
all the information in the system is irretrievably lost. Obviously this is not an
acceptable business solution.
In Java it is possible to save objects in an ordinary file and reload them, but
this is not suitable for the kind of large-scale persistence that would be
required in an enterprise application. For that we need a database.
Computer databases were developed in the 1960s. A database is a collection
of records, kept in persistent storage such as disk, and organised in a
systematic way that allows efficient storage and retrieval of records.
Applications that use a database do not interact with it directly, but via a
database management system (DBMS), which is independent of any
particular application, and which hides all the details of the database
implementation. In terms of the deployment diagram in Figure 7 the DBMS
would run on the database server component.
To explain how our system could be made persistent it is easiest to begin by
outlining how data is stored in a database, and then to explain how the stored
data corresponds to the business objects in an application.
In a database the data is normally stored in tables. For example, data about
room types could be held as in Table 8.

Table 8 ROOM_TYPE

ID (integer) KIND (text) COST (decimal)


1 SINGLE 80.00
2 DOUBLE 120:00
3 DOUBLE 150.00
… … …

The rows in this table each represent a record, containing the data for one
particular room type. The columns each contain data of one specific type,
such as text or decimal. The ID column is special because it uniquely
identifies each record. A column that uniquely identifies the rows of a
database table is called a primary key.
The table for rooms might look like Table 9.

198
6 The way forward

Table 9 ROOM

ID (integer) ROOM_TYPE_ID (integer)


101 1
102 1
… …
201 3
… …

This has a column named ID that is the primary key. It also has a column
ROOM_TYPE_ID that holds keys from a different table. This is a foreign
key. Notice that the foreign key enables us to find the room type of any given
room.
How can database tables be used to make business objects persistent? We
map the business objects to database storage in the following way.
. For each class of business objects there is a corresponding database table.
. Each instance variable is represented as a column of the table.
. Each object belonging to the class is represented as a row of the table.
So, for example, the table ROOM_TYPE above corresponds to the class
RoomType in the hotel system. Figure 19 shows a UML object diagram
corresponding to the three rows in Table 9.

: RoomType
iD = 1
kind = "SINGLE"
cost = 80.00

: RoomType
iD = 2
kind = "DOUBLE"
cost = 120.00

: RoomType
iD = 3
kind = "DOUBLE"
cost = 150.00

Figure 19 Objects corresponding to database rows


One thing is still missing: how does our Java program communicate with the
DBMS? The underlying communication uses JDBC, as shown in the
deployment diagram of Figure 7. However using JDBC directly, while not JDBC is a Java API for
difficult, is inconvenient because the statements recognised by the DBMS are communication with database
not object-oriented. management systems.

Fortunately the Java Persistence API (JPA) hides the low-level details and
lets us interact with the database in an object-oriented way. To make the class

199
Unit 12 The case study: part 3

RoomType persistent, only relatively minor changes are needed. Here is the
RoomType class made into an entity class, that is, one that JPA will map to a
database table. All this is achieved with two annotations: @Entity which
declares this is an entity class, and @Id which declares id as the primary
key.
The interface type @Entity
Serializable just marks the public class RoomType implements Serializable {
class as one whose objects can
be converted into a storable @Id
format. private Integer id;
private String kind;
private BigDecimal cost;
public RoomType() {
}
// getters and setters
}

Here is the code of Room, which introduces two new annotations.


@JoinColumn indicates an instance variable while roomType represents a
foreign key. @ManyToOne declares that the relationship between Room and
RoomType is many to one, as you probably guessed. Notice that the foreign
key in the database table corresponds to an association in the structural
model.
@Entity
public class Room implements Serializable {
@Id
private Integer id;
@JoinColumn
@ManyToOne
private RoomType roomType;
public Room() {
}
// getters and setters
}

6.2 Distribution – JavaServer Faces (JSF)


JSF is a popular framework for developing browser-based user interfaces.
JSFs are part of Java EE and run in a web container. In the deployment
diagram of Figure 7 they would be part of the presentation component.
JSF, like many frameworks, is based on MVC and supports a very clear
separation between presentation and business logic. Figure 20 gives an
overview of how the MVC roles relate to the JSF framework.
To demonstrate the essential features of the framework we will use JSF to
develop the converter bean example of Unit 10 into a browser-based web

200
6 The way forward

web container
controller
FacesServlet
<<transparent to developer>>

view model
xhtml pages backing beans

Figure 20 JSF and MVC

application. Figure 21 shows a view of the running application as it would


appear in a browser window.

Figure 21 The converter displayed in a browser

Controller
In JSF the role of the controller is taken by a servlet called the FacesServlet,
which is a component generated automatically by the container. The
FacesServlet remains behind the scenes and the developer never needs to be
aware of it.

Model
The role of the model is taken by a Java EE component called a managed
bean. Below is the code of the managed bean, which is very similar to the
Converter EJB you saw in Unit 10. The key differences are:

. An annotation @Named has been added. This will enable the view
component to communicate with the bean by using its name.
. Instead of the convertLandArea method being passed arguments area
and unit it operates on instance variables with the same names.
. Instead of the method returning a value it sets an instance variable called
result.

201
Unit 12 The case study: part 3

. All the instance variables have getters and setters.


@Stateless
@Named
public class ConverterBean {
private double area;
private String unit;
private String result;
public void convertLandArea() {
if (unit.equals("acres")) {
result = String.valueOf(area * 0.40468) +
"ha";
} else if (unit.equals("ha")) {
result = String.valueOf(area * 2.471) +
"acres";
} else {
result = "Unit not recognised";
}
}
public double getArea() {
return area;
}
public void setArea(double area) {
this.area = area;
}
public String getUnit() {
return unit;
}
public void setUnit(String unit) {
this.unit = unit;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
}

View
The role of the view is taken by a page written in XHTML, a language for
defining web pages. Below is the code of the view. You don’t need to follow
all the details or the syntax, but if you look at this code and that of the
Converter bean you should be able to get the idea of how the view

202
6 The way forward

communicates with the bean and how it creates the GUI components that are
displayed in the browser window.
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0
Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/
xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>Converter</title>
</h:head>
<h:body>
<h:form>
Please enter the area
<h:inputText value="#{converterBean.area}"/>
<br/>
Please enter the unit
<h:inputText value="#{converterBean.unit}"/>
<br/>
<h:commandButton value="convert"
action="#{converterBean.convertLandArea()}"/>
</h:form>
<h:outputText value="#{converterBean.result}"/>
</h:body>
</html>

This ends our glimpse into how the next and subsequent iterations might
develop. We have demonstrated the concepts behind Java EE technologies
that offer solutions to the twin problems – persistence and distribution – that
are crucial to enterprise applications.
It is only a start of course, but it points to the way forward. A web browser
might replace the user interface. A mixture of JSF pages and managed beans
might replace the Facade. EJBs might implement the business functions.
Finally the business objects, such as guests, rooms, and reservations, might
be represented by entity classes that can be made persistent in the database.

6.3 Summary of section


In this section you began by looking at a number of different ways in which
this first iteration of the hotel system is incomplete. Two aspects in particular
were identified – the lack of persistence and the fact that the system is not
distributed.
You were given a brief introduction to how data can be made persistent by
storing it in database tables, and saw how the JPA allows Java programs to
interact with a database. Each Java entity class corresponds to a table in the

203
Unit 12 The case study: part 3

database. The columns correspond to attributes and the rows to instances of


the class. These concepts were demonstrated by a practical activity.
You then met JSF, a popular framework based on MVC, and saw how
managed beans and XHTML pages might be used to provide the hotel system
with a browser-based interface that would allow users to access it via the
internet.

204
7 Summary

Summary 7
This unit looked at the implementation of a first iteration of the hotel system.
It began by considering three architectural views – the logical view, the
process view and the deployment view – and some of the typical artefacts
from each view.
A logical view was then developed for the first iteration. This was based on
MVC but also included a Facade layer to further decouple the user interface
from the business layer.
Next the unit explored in some detail how attributes, associations and
operations can be implemented in Java, and how to apply unit tests to
methods.
The Facade software pattern was explored more deeply and you learnt how
coupling between user interface and the business layer can be reduced, not
just by hiding supplier objects from the user interface but also by having the
user and interface trade in different types of data.
Having designed and coded an iteration, we must verify and validate the
implementation. You saw examples of system and acceptance testing based
on use cases.
As well as functional testing we must also see whether quality attribute
requirements have been met. As an example you were asked to formulate a
quality attribute scenario for usability, and see whether the implementation
met the associated measure. You were also asked to reflect on how well the
architecture chosen followed tactics for flexibility.
Finally we explored ways in which this first iteration is incomplete. Two of
the main issues are the lack of persistence and distribution, and you saw
briefly how these might be addressed in future iterations.
After studying this unit you should be able to:
. discuss architectural views and know some of the artefacts typically found
in each view
. understand how attributes, associations and operations can be
implemented in a language such as Java
. follow how unit, system and acceptance tests can be specified and carried
out
. understand the purposes of the Facade software pattern
. appreciate how a quality attribute scenario can be developed and applied
. understand how tactics for quality attributes can be used to look for
improvements in an architecture
. understand some ways of reviewing an iteration and planning changes to
be made in following iterative cycles.

205
Unit 12 The case study: part 3

References
Bass, L., Clements, P. and Kazman, R. (2003) Software Architecture in
Practice, 2nd edn, Boston, Addison Wesley.
Bass, D. L., Clements, D. P. and Kazman, D. R. (2012) Software Architecture
in Practice, 3rd edn, Upper Saddle River, NJ, Addison Wesley.
Gamma, E., Johnson, R., Vlissides, J. and Helm, R. (1995) Design Patterns:
Elements of Reusable Object-Oriented Software, Wokingham, Addison
Wesley.

206
Acknowledgements

Acknowledgements
Grateful acknowledgement is made to the following sources.
Every effort has been made to contact copyright holders. If any have been
inadvertently overlooked, the publishers will be pleased to make the
necessary arrangements at the first opportunity.

Unit 9
Figures
Figure 1. © Kmiragaya www.dreamstime.com.
Figure 18. Taken from www.thebushcraftstore.co.uk/european-2-pin-round-
pin-travel-plug-adapter-2888-p.asp.
Figure 27. Taken from www.craftmaterialsupplies.co.uk.
Figure 28. Courtesy Clock Prints at www.clockprints.zenfolio.com.

Unit 10
Figures
Figure 1. www.flickr.com/photos/transphormetic/2677625658. This file is
licensed under the Creative Commons Attribution-Noncommercial-ShareAlike
Licence.

207
Index

Index
Index entries in bold type indicate requirements and architectural blackboard data-centred style 25
glossary entries. decisions 12–14 bottlenecks 79
reuse of 19, 21–30 boundary testing 138
acceptance testing 127, 132, 132–
stakeholders, views and browsers 21, 86
3, 133–4
viewpoints 15–17 accessing RESTful web service
hotel system 191–2
architecture specialisation 29 91
activity diagram 166–7
architecture specification 28–9 browser-based user interface
Adapter design pattern 31, 31–2,
area converter 87–8, 89–91, 200–3 200–3
35, 36
artefacts 71–7, 193–4 bursty events 72–3, 74–5
adapters (wrappers) 64
assertion errors 181 business layer 38, 187
agility 103
assertion mechanisms 103, 103–4, business services 68–9
compatibility with architecture
120–6 call-return style 21, 22, 93, 170
11–12
assertions for withdrawing called classes 128–9
airline reservation system 139–42,
operation 121–3 callee 22
144
class invariants 124–6 calling classes 128–9
Alexander, C. 9, 35
enabling assertions 121 car park monitoring and display
allocation view 15
writing assertions in Java 120–1 systems 41–3, 73, 76–7
analysis patterns 19, 173
associations 171, 176–9 case analysis 138, 140, 144
analytical completeness 113
assume–guarantee contract 62, 63 Cavano, J.P. 106–8
and operator 148
assumptions 61, 61–2, 63 Chen, L. 13
API classes 150–1
asynchronous message exchange Chidamber, S.R. 149–50
applet container 86
protocols 25 class diagrams 46, 47, 162, 163
applets 86
attacks, likelihood of 109 class invariants 124–6, 125
application clients 86, 88, 93–4
attributes 171–2 class variables 171
application environment 13
automated testing tools 116, 145 client–server style 7, 8, 21, 21–2,
appropriate quality 105–6
balanced approach to integration 93
see also quality
testing 130–1 clocks 37
arbitrating resources 79, 80
banking system withdrawal code increment 116, 117
architectural descriptions 16–17
operation 120–6 codifying patterns 34–6
architectural patterns 9
basis-path testing 143 cohesion 82
architectural styles 7, 21, 21–7,
Bass, L. 14–15, 71, 78, 81, 193 maximising 81, 82, 95, 195
30, 92–3
Beck, K. 115 cohesiveness 150
see also under individual styles
behavioural assumptions 61–2 collection of references 177–8
architectural views 14, 14–15
behavioural models 162 comments 124
hotel system 161, 162–8, 170
behavioural patterns 35 completeness 113, 113–14
stakeholders, viewpoints and
Beizer, B. 132 complexity measurement 146–53
15–17
big design up front (BDUF) 11 component-based development
architecturally significant
BigDecimal classes 173–4 (CBD) 57, 59, 131
requirements (ASRs) 13–14, 16
binding 82–3 component and connector view 15
architecture 5, 6–18
dynamic 128–31 component testing 127, 128, 131
and agility 11–12
late 82–3, 95, 195 components 57, 58–65
analogy with buildings 17
black-box testing 137, 137–46 collection in product-line
choosing for the hotel system
Money::plus operation 174–5 process 29
169–71
problems with 144–5 example EJB 87–8
defining software architecture 6
techniques 138–9 interacting 63–4
requirements and 9–11
using partitioning 139–42 interfaces 57, 59–62

209
objects as 62–4 demand entity classes 200, 203
representing in UML 59–60 managing 78, 80 entity-oriented services 69
selection and specialisation 29 matching resources to 79, 80 enumerated type (enum) 172–3
services vs 66–7 dependency injection 88 environment 71–7, 193–4
composition 32 dependent errors 129–30 equality testing 151–2
concerns 15, 15–16 deployment diagram 167, 170 equivalence partitioning 138–42
concurrent processing 79 deployment model 167, 167–8 error guessing 139
configuration file 83 deployment view 14, 15, 17 event handlers 28
connection, new 46–7 hotel system 161, 167–8, 170 execution complexity 146
connectors 15 depth-of-inheritance-tree (DIT) expectations 105–6
consistency 112, 112–13 metric 149, 150 exploratory programming 119
constraints 13 design by contract (DbC) 64, 103, extreme programming (XP) 103
constructors, assertions in 125–6 118, 126–7, 133, 137, 139 Facade pattern 169–70, 186–8, 197
containers 85, 85–6 worked example 119–26 FacesServlet 201
web container 86, 91, 93–4 design patterns 5, 19, 31–50 factory 44, 83
context dependencies (required Adapter 31–2, 35, 36 Factory design pattern 35, 43, 44–
interface) 59, 62, 63 codifying 34–6 50
controller 37, 38, 201 Facade 169–70, 186–8, 197 Factory Method pattern 47–50
converter beans 87–8, 200–3 Factory 35, 43, 44–50 Falessi, D. 12
coordination services 68–9 MVC 35, 36–40, 169, 200, 201 featuritis 118
core features 13 Observer 27, 35, 40–3 fence testing 138
core system 169, 186–7 Singleton 35, 43–4 figure drawing 128–31
correctness 106, 107, 109 template for documenting 35–6, filters 8, 8–9
coupling 81 38–9, 40–1, 43–4, 45–6, 48, final variables 171–2
loose 66 186 find, bind and invoke model 67–8
minimising 81–2, 94–5, 195 detail of implementation 137, 138 fit criteria 73, 113, 190–1
coupling-between-objects (CBO) see also white-box testing fixture 175
metric 149, 150 development view 14, 15 flexibility 39, 78
creational patterns 35 developmental testing 127, 128– SQF 108
customer expectations 105–6 33 tactics for 81–3, 94–5, 195
cyclomatic-complexity metric integration testing 119, 127, for loops 147, 148
143, 147, 147–9, 151–2 128–31, 134, 191 foreign key 199, 200
data-centred style 21, 24, 24–5, system testing 127, 128, 132–3, frameworks 27, 27–8, 29–30
93 134, 189–92 functional requirements 10, 12, 13,
data-flow style (pipe-and-filter distribution 197, 200–3 71, 113
style) 8–9, 21, 24 do-while loops 147, 148 system testing for hotel system
data loss 72, 73, 75 document factory 48–50 189–92
data types 171, 172–5 domain analysis 28 functional view see logical view
database data-centred style 24, dynamic binding 128–31 Gamma, E. 115
24–5 dynamic systems development Gang of Four 34–5
database management system method (DSDM) 115 generalisation 34
(DBMS) 46, 198, 199 efficiency 106, 107, 108 getter methods 89–91, 123–4, 202
database tables 198–9 EJBs (Enterprise JavaBeans) 85, Gilb, T. 109
databases 118, 198–200 203 graphical user interfaces 28, 88
deadline 72, 75, 79 EJB container 86 guarantees 61, 61–2, 63
decision points 147 example 87–8 heuristic evaluation 109, 127
defects per thousand lines of code electronic engineering 58 hotel system 157-206
(defects per KLOC) 109 enabling assertions 121 architectural views 161, 162–8,
defensive programming 138, 138– enterprise architectures 57, 84-96 170
9 enterprise service bus (ESB) 26

210
Index

browser-based user interface initialisation testing 132 LAMP stack 6–7


200–3 inner class idiom 122–3 land area converter 87–8, 89–91,
cancel reservation 162, 165, input data space 138–9 200–3
189, 190, 191 determining 139–40 late binding 82–3, 95, 195
check in guest 162, 165, 166, partitioning into subdomains latency 72, 73, 79
167 140–2 layered style 7, 21, 22, 22–3, 93,
check out guest 162, 165 instance variables 171, 200, 202 169
createGuest operation 182 integration 64 lazy instantiation 44
createReserverPayer operation and release in product-line lines-of-code (LOC) metric 146,
180–2 process 29 148–9
Facade pattern 169–70, 186–8, integration testing 119, 127, 128, lists 118, 178
197 128–31, 134, 191 local inner classes 122–3
implementing a first iteration integrity 106, 107, 108, 109 local variables 122
161, 169–85 interacting components 63–4 location transparency 67
implementing and testing interaction diagrams 162, 164 logical view 14, 15, 16
operations 179–84 interface testing 132 hotel system 161, 162–6, 170
issues that need addressing 197– interfaces 33–4, 57, 59, 59–62, 66 login server 7
8 remote interface 87 loose coupling 66
Java EE architecture 91–5 representing in UML 60–1 maintainability 106, 108, 109
make reservation 162, 164, 165, as sets of behavioural managed bean 201–2
189–90, 190–1 assumptions 61–2 McCabe, T.J. 143, 147
makeReservation operation 182– user see user interface McCall, J.A. 106–8
4 interoperability 108 mean time to change (MTTC)
Money class 172, 173–4 invariant conditions 104 109
Money::plus operation 174–5 IP address 83 measure (response measure) 71–7,
persistence 197, 198–200 iterative approaches 115 193–4
Reservation class 172, 177, 178– Java Community Process (JCP) minimal approach to integration
9 84–5 testing 130, 131
ReserverPayer 177, 178–9 Java Persistence API (JPA) 85, miss rate 72, 73, 75
Room class 172, 176–7 199–200 ‘mock’ objects 182
RoomKind 172–3 Java Platform, Enterprise model 37, 38, 201–2
RoomType 176–8, 198–200 Edition (Java EE) 57, 84–96, Model-view-controller (MVC)
special offer information 93–4 167 pattern 35, 36, 36–40, 169, 200,
verification and validation 189– architecture for the hotel system 201
96 91–5 modifiability 78
HTTP 89 example EJB 87–8 see also flexibilit
idioms 19, 34 example service 88–91 module view 15
inner class idiom 122–3 technologies comprising 85 modules, kept small 82, 95, 195
if statements 147–8 Java Specification Participation multiplicities 176–9
ilities 13 Agreement (JSPA) 84–5 association end with multiplicity
see also quality attributes Java Swing 27, 28, 38 many 177–9
implicit invocation (notification) JavaServer Faces (JSF) 85, 200–3 association end with multiplicity
style 21, 27 JDBC 167–8, 199 one 176–7
independent components style 21, jitter 72, 73, 75 Musa, J. 135
25, 25–6 JUnit testing 115–16, 175 new connection 46–7
independent errors 130 Kemerer, C.F. 149–50 non-functional requirements 10,
independent paths 143, 147–8 key–value pairs 179 12–13, 71, 113
inertial convention 124 Kruchten, P. 14 quality attributes and the hotel
inheritance 32, 149 lack-of-cohesion-in-methods system 192–5
and polymorphism 128–31 (LCOM) metric 150 notification style 21, 27

211
number-of-children (NOC) initiation 28 quality 105–6, 107–8
metric 149, 149–50 process 28–30 specific-requirements capture for
Nuseibeh, B. 11 product operation requirements 107 new products 29
object diagrams 162 product revision requirements 107– requirements-based testing 127,
object-oriented metrics 149–52 8 132
objects product transition requirements requirements patterns 19
as components 62–4 107–8 requirements specification 105, 113
‘mock’ 182 protected visibility 171 managing 79, 80
observable (subject) 40 protocol testing 132 resources
Observer design pattern 27, 35, prototyping 115 matching to demand 79, 80
40–3 hotel system 161, 169–85 response 71–7, 193–4
operator testing 132 provided interface 59, 62, 63, 64 response-for-a-class (RFC) metric
or operator 148 public visibility 171 150
outliner view 114 publish–subscribe style response measure 71–7, 193–4
package diagrams 162, 164 (notification style) 21, 27 response set 150
package visibility 171, 172 publisher 27 restart testing 132
packages 163, 165 qualified associations 176, 177, RESTful web services 89, 89–91,
partitioning 138, 138–9 178–9 197–8
using for black-box testing 139– quality 105–10 retrofitting unit tests 119
42 meaning of 105–6 reusability 67, 108
path parameters 90 SQFs 106–9 reuse 5, 19–20
pattern catalogues 34–5 quality attribute scenarios 57, 71, of architecture 19, 21–30
peer-to-peer (P2P) style 8, 21, 23, 71–5, 193–4 of design 19, 31–50
23–4 quality attributes (ilities) 13, 71–83 on different levels 19–20
performance 7, 78 hotel system 192–5 safe approach to integration testing
scenarios 72–5 tactics for 57, 77–83, 94–5, 195 129–30, 131
tactics for 77–80 Quantity pattern 173 scenarios 14
performance testing 132 random testing 139 quality attribute scenarios 57,
periodic events 72, 74 realisation 33, 33–4 71–5, 193–4
persistence 197, 198–200 records 198 search engine 74, 77
physical view 14, 15 refactoring 118 security 12
pipe 8, 8–9 registry 68 security testing 132
pipe-and-filter style (data-flow regression testing 118, 127, 133– self-consistency 112–13
style) 8–9, 9, 21, 24 4, 145 sequence diagrams 46, 47
polymorphism 128, 128–31 reliability 8, 12, 106, 107, 135–7 sequences of commands 144–5
portability 72, 108–9 remote interface 87 serializable interface type 200
postconditions 63, 103–4 remote server 12 service consumer 66
bank withdrawal operation 120– replaceability 63 service orchestration 69
6 repository 24, 25 service-oriented architecture
preconditions 63, 64, 103–4 required interface 59, 62, 63, 64 (SOA) 21, 26, 57, 66, 66–70, 83,
bank withdrawal operation 120– requirements 105–6 93
6 and architectural decisions 12– advantages 69–70
primary key 198, 198–9, 200 14 composing an application 69
primary software quality factors architecturally significant 13–14, find, bind and invoke model 67–
109 16 8
private visibility 171 architecture and 9–11 service provider 66, 68
process view 14, 15, 16–17 functional 10, 12, 13, 71, 113, service requester 66
hotel system 161, 166–7, 170 189–92 services 66–70
product 44 non-functional 10, 12–13, 71, components vs 66–7
product lines 28, 28–30 113, 192–5 definition 66

212
Index

example service in Java EE 88– switch statements 147, 148 unit testing 115, 115–16, 127, 128,
91 syngo.via 29 134
kinds of service 68–9 system capacity, managing 79, 80 retrofitting unit tests 119
sets 178 system complexity 146–53 unreachable statements 143
setter methods 202 system descriptions 111 usability 106, 107, 108–9
Siemens Healthcare Sector 29 consistency and self-consistency hotel system 193–5
signatures 33 112–13 scenarios 75–7, 193–4
Singleton design pattern 35, 43, system testing 127, 128, 132–3, usability testing 109, 127, 127–8
43–4 134 use cases 162
six-part model for quality attribute hotel system 189–92 packages 163, 165
scenarios 71–7, 193–4 tables, database 198–9 supporting in the Facade pattern
Skype architecture 7–8 tactics 57, 77, 77–83 187–8
software architecture see flexibility 81–3, 94–5, 195 user-command testing 132
architecture performance 77–80 user-defined types 172, 172–4
software quality see quality task-oriented services 69 user interface
software quality factors (SQFs) templates 20 browser-based 200–3
106, 106–9 template for documenting design graphical 28, 88
primary 109 patterns 35–6, 38–9, 40–1, 43– hotel system 169, 186–8, 193–5,
sources 71–7, 193–4 4, 45–6, 48, 186 200–3
specific-requirements capture 29 temporarily invalid state 125 MVC design pattern 36–40
specification 11, 137, 138 test cases 104, 174–5 testing 39, 118, 127–8
see also black-box testing strategies for creating 134–46 user stories 191–2
spiral process 11, 19 test coverage 118 utility services 68–9
sporadic events 72, 74 test-driven development (TDD) validation 111, 111–14, 134
stakeholders 15–17 103, 115–19, 126–7, 133, 137 hotel system 189–96
standard communication protocols benefits of 118 variables 171–2
67 hotel system 179–84 instance 171, 200, 202
standards limitations and pitfalls 118–19 local 122
Software Engineering – Product preparing for 115–16 verification 111, 111–14, 134
Quality (ISO 9126) 106–7 steps 116–18 hotel system 189–96
Standard for System and testability 108 view 37, 38, 202–3
Software Verification and testing 103–4, 111, 115–53 viewpoint 17
Validation (IEEE 1012) 111 adequacy of 144–6 visibility 171, 172
Systems and Software design by contract 119–26 waterfall approach 10
Engineering – Architecture general categories of 126–34 web browsers see browsers
Description (ISO/IEC/IEEE measuring complexity 146–52 web container 86, 91, 93–4
42010) 17 operations in hotel system 179– weighted-methods-per-class
start-up testing 132 84 (WMPC) metric 150, 151
statelessness 67 strategies for creating test cases while loops 147, 148
statistical techniques 135–7 134–46 white-box testing 137, 137–8
stimuli 71–7, 78, 193–4 user interfaces 39, 118, 127–8 basis-path testing 143
stochastic events 72 see also under individual problems with 145–6
stress testing 132 methods techniques 143
structural complexity 146–53 throughput 72, 73, 75, 79 Wikimedia architecture 7
structural models 16–17, 162 time between failures 135–7 Windows command prompt 8–9
structural patterns 35 to-do list 118 withdrawing money operation 120–
subdomains 138, 140–2 try statements 147, 148 6
subscribers 27 twin-peaks model 11, 11–12, 19 word processors 114
substitutability 62 Unified Process (UP) 9, 14 wrappers (adapters) 64
super nodes 7–8 XHTML pages 202–3

213

You might also like