You are on page 1of 14

Chapter 1: The Context of Software Development

In this chapter, we describe the components of a computer system and discuss the general
ideas behind computer languages. The overview provided in this chapter will help prepare you
for future chapters. You can skip the chapter in the first reading and return to it when you have
a better understanding of programming.

OBJECTIVES
After you have read and studied this chapter, you should be able to:

• Discuss the two major components of a computer: hardware and software. • Describe the
six parts of hardware: CPU, primary memory, secondary storage, input system, output
system, and communication system.
• Describe the two major categories of software: system software and application software.
• Describe the evolution of computer languages from machine languages to assembly
languages, and to high-level languages. • Discuss four different paradigms of computer
languages: procedural, object-oriented, functional, and logic.
COMPUTER SYSTEM

A computer system is made of two major components: hardware and software. The computer
hardware is the physical equipment. The software is the collection of programs (instructions)
that allow the hardware to do its job.

COMPUTER HARDWARE

The hardware of a computer system consists of six parts: a central processing unit (CPU), main
memory, secondary storage, the input system, the output system, and the communication
system. These components are connected together by what is called a bus. Figure 1.1 shows
these six components and their connection.

Central Processing Unit (CPU)

The central processing unit (CPU) consists of the arithmetic-logical unit (ALU), the control
unit, and a set of registers to hold data temporarily while being processed. The control
unit is the traffic cop of the system; it coordinates all the operations of the system. The
ALU executes instructions such as arithmetic calculations and comparisons among data.
Figure 1.2 shows the general idea behind a central processing unit.
Primary Memory Primary

Primary Memory is where programs and data are stored temporarily during processing. The
contents of primary memory are lost when we turn off the computer. Figure 1.3 shows primary
memory in more detail. Each storage location in memory has an address, much like a street
address, that is used to reference the memory’s contents. The addresses in Figure 1.3(a) are
shown on the left as numbers ranging from zero to (n – 1), where n is the size of memory. In
Figure 1.3(b) the address is shown symbolically as x.
Generally, each address refers to a fixed amount of memory. In personal computers, the
amount of storage accessed is usually one, two, or four bytes. In large computers, it can be
many bytes. When more than one byte is accessed at a time, word rather than byte is usually
used for the memory size.

In general, primary memory is used for three purposes: to store the operating system, to store
the program, and to store data. The type of data stored is dependent on the application. In
Figure 1.3, we demonstrate three different types of data: an integer (256), a real number
(15.34), and a string (Hello).

Secondary Storage

Programs and data are stored permanently in secondary storage. When we turn off the
computer, our programs and data remain in the secondary storage ready for the next time we
need them. Examples of secondary storage include hard disks, CDs and DVDs, and flash drives
(Figure 1.4).

Input System

The input system is usually a keyboard where programs and data are entered into the
computer. Examples of other input devices include a mouse, a pen or stylus, a touch screen, or
an audio input unit (Figure 1.5).
Output System

The output system is usually a monitor or a printer where the output is displayed or printed. If
the output is displayed on the monitor, we say we have a soft copy. If it is printed on the
printer, we say we have a hard copy (Figure 1.6).

Communication System

We can create a network of computers by connecting several computers. Communication


devices are installed on a computer system for this purpose. Figure 1.7 shows some of these
devices.

COMPUTER SOFTWARE

Computer software is divided into two broad categories: system software and application
software. This is true regardless of the hardware system architecture. System software manages
the computer resources. Application software, on the other hand, is directly responsible for
helping users solve their problems.

System Software

System software consists of programs that manage the hardware resources of a computer and
perform required information processing. These programs are divided into three groups: the
operating system, system support, and system development.

Operating System

The operating system provides services such as a user interface, file and database access, and
interfaces to communication systems. The primary purpose of this software is to operate the
system in an efficient manner while allowing users access to the system.
System Support

System support provides system utilities and other operating services. Examples of system
utilities are sort programs and disk format programs. Operating services consist of programs
that provide performance statistics for the operational staff and security monitors to protect
the system and data.

System Development

System development software includes the language translators that convert programs into
machine language for execution, debugging tools to assure that the programs are error-free,
and computer-assisted software engineering (CASE) systems that are beyond the scope of this
book.

Application Software

Application software is broken into two categories: general-purpose software and application
specific software.

General-Purpose Software

General-purpose software is purchased from a software developer and can be used for
more than one application. Examples of general-purpose software include word
processors, database management systems, and computer-aided design systems. These
programs are called general purpose because they can solve a variety of user computing
problems.

Application-Specific Software

Application-specific software can be used only for its intended purpose. A general ledger
system used by accountants and a material requirement planning system used by
engineers are examples of application-specific software. They can be used only for the
task they were designed for; they cannot be used for other generalized tasks.
COMPUTER LANGUAGES

To write a program for a computer, we must use a computer language. Over the years,
computer languages have evolved from machine to symbolic to high-level languages and
beyond. A timeline for computer languages is seen in Figure 1.8.

Machine Languages

In the earliest days of computers, the only programming languages available were machine
languages. While each computer still has its own machine language, which is made of streams of
0s and 1s, we no longer program in machine language.

Symbolic Languages

It became obvious that not many programs would be written if programmers continued to work
in machine language. In the early 1950s, Grace Hopper, a mathematician, and a member of the
United States Navy, developed the concept of a special computer program for converting
programs into machine language (see Figure 1.9).

Her work led to the use of programming languages, which simply mirrored the machine
languages using symbols, or mnemonics, to represent the various machine language
instructions. Because they used symbols, these languages were known as symbolic languages. A
special program called an assembler is used to translate symbolic code into machine language.
Because symbolic languages must be assembled into machine language, they soon became
known as assembly languages. This name is still used today for symbolic languages that closely
represent the machine language of their computer.
High-Level Languages

Although symbolic languages greatly improved programming efficiency, they still required
programmers to concentrate on the hardware they were using. Working with symbolic
languages was also very tedious because each machine instruction had to be individually coded.
The desire to improve programmer efficiency and to change the focus from the computer to
the problem being solved led to the development of high-level languages.

High-level languages are portable to many different computers, which allows the programmer
to concentrate on the application problem at hand rather than the intricacies of the computer.
High level languages are designed to relieve the programmer from the details of the assembly
language. However, high-level languages share one thing with symbolic languages: They must
be converted to machine language. This process is called compilation.
The first widely used high-level language, FORTRAN (Formula Translation), was created by John
Backus and an IBM team in 1957. Following soon after FORTRAN was COBOL (Common Business
Oriented Language). Admiral Grace Hopper was again a key figure, this time in the development
of the COBOL business language.
Over the years, several other languages—most notably BASIC, Pascal, Ada, C, C++, and Java—
were developed. Today, one of the popular high-level languages for system software and new
application code is C++, which we discuss in this book.

PARADIGMS IN C++ LANGUAGE

Our discussion of four paradigms might lead you to wonder where the C++ language stands. C++
is an extension to the C language and is based on the procedural paradigm. However, the
existence of classes and objects allows the language to be used as an object-oriented language.
In this book we use C++ mostly as a procedural paradigm in early chapters (except for
input/output that are done using objects). However, we use the language as an objectoriented
paradigm after the introductory chapters.

Object-Oriented Paradigm

In the procedural paradigm, the often-used procedures can be created and saved. We then
apply a subset of these procedures to the corresponding data packages to solve our problem.
One thing that is obvious in this paradigm is that there is no explicit relationship between the
set of procedures and the set of data packages. When we want to solve a problem, we need to
choose our data package and then go and find the appropriate procedure(s) to be applied to it.

The object-oriented paradigm goes further and defines that the set of procedures that can be
applied to a particular type of data package needs to be packaged with the data. The whole is
referred to as an object. In other words, an object is a package containing all possible operations
that can be applied to a particular type of data structure. This is the same concept we find in
some physical objects in our daily life. For example, let us think about a dish-washing machine
as an object. The minimum operations we expect from a dishwasher are washing, rinsing, and
drying. All these operations are included in any typical dish-washing machine. However, each
time we load the machine with a different set of dishes (same as a different set of data). We
need to be careful, however, not to load the machine with a load it is not designed for (not to
wash clothes, for example, in the dish-washing machine).
In the real world, all the hardware necessary for an operation is included in an object; in the
object oriented programming world, each object holds only data, but the code that defines the
procedures is shared. Figure 1.12 shows the relationship between the procedures and data in
the object-oriented paradigm.
Functional Paradigm

In the functional paradigm, a program is a mathematical function. In this context, a function is a


black box that maps a list of inputs to a list of outputs. For example, adding numbers can be
considered as a function in which the input is a list of numbers to be added and the output is a
list with only one item, the sum. In other words, the functional paradigm is concerned with the
result of a mathematical function. In this paradigm, we are not using commands and we are not
following the memory state. The idea is that we have some primitive functions, such as add,
subtract, multiply, divide. We also have some primitive functions that create a list or extract the
first element or the rest of the elements from a list. We can write a program or a new function
by combining these primitive functions.

Logic Paradigm

The logic paradigm uses a set of facts and a set of rules to answer queries. It is based on formal
logic as defined by Greek mathematicians. We first give the program the facts and rules before
asking queries.
PROGRAM DESIGN

Program design is a two-step process that requires understanding the problem and then
developing a solution. When we are given the assignment to develop a program, we are given a
program requirements statement and the design of any program interfaces. In other words, we
are told what the program needs to do. Our job is to determine how to take the inputs we are
given and convert them to the outputs that have been specified. To understand how this
process works, let us look at a simple problem.

Understand the Problem


The first step in program design is to understand the problem. We begin by reading the
requirements statement carefully. When we fully understand it, we review our understanding
with the user. Often this involves asking questions to confirm our understanding. For example,
after reading our simple requirements statement, we should ask several clarifying questions.

If we don’t clarify the problem—that is, if we make assumptions about the input or the output
— we may supply the wrong answer. To answer our questions, we need to process integers
arranged in any sequence. There is no limit as to the number of integers.

As this example shows, even the simplest problem statements may require clarification. Imagine
how many questions must be asked for a program that contains hundreds or thousands of
detailed statements.

Develop the Solution

Once we fully understand the problem and have clarified any questions we may have, we
develop a solution in the form of an algorithm. An algorithm is a set of logical steps necessary
to solve a problem. Algorithms have two important characteristics: first, they are independent
of the computer system. This means that they can be used to implement a manual system in an
office as well as a program in a computer. Second, an algorithm accepts data as input and
processes the data into an output.

To write the algorithm for our problem, we use an intuitive approach, calling not only on the
problem statement but also our knowledge and experience. We start with a small set of five
numbers: Once we have developed a solution for five numbers, we extend it to any number of
integers.

We begin with a simple assumption: The algorithm processes the numbers one at a time. We
name the algorithm FindLargest. Every algorithm has a name to identify it. FindLargest looks at
each number in turn without knowing the values of the others. As it processes each number, it
compares it to the largest number known to that point and determines if the new number is
larger. It then looks at the next number to see if it is larger, and then the next number and the
next number until all the numbers have been processed. Figure 1.15 shows the steps in
determining the largest among five integers.

The algorithm requires that we keep track of two values, the current number and the largest
number found. We determine the largest number using the following steps.

Step 1: We input the first number, 13. Since largest has no value, we set it to the value of the
first number.

Step 2: We input the second number, 7. Since 7 is less than 13, the value of largest does not
need to be changed.

Step 3: We input the third number 19. When we compare 19 to the value of largest, 13, we see
that 19 is larger. We therefore set largest to 19.

Step 4: We input the fourth number, 29. When we compare 29 to the value of largest, 19, we
see that 29 is larger. We set largest to 29.

Step 5: We input the fifth number, 23. Because it is smaller than 29, largest does not need to be
changed. Because there is no more input, we are done, and we have determined that the
largest value is 29.

Step 6: We output the value of largest, which is 29.

Algorithm Generalization

The algorithm shown in Figure 1.15 does not quite solve our original problem definition because
it only handles five numbers. To make it work for all number series, we need to replace steps 2
through 5 to process an undetermined number of values. This requires that we generalize the
statements so that they are the same. We can do this with a minor rephrasing of the statements
as shown below.

We then include the rephrased statement in a repeat statement that executes the steps until all
numbers are processed. The resulting algorithm is shown in Figure 1.16.
It is important to realize that the design is done before we write the program. In this respect, it
is like the architect’s blueprint.
It is the job of the programmer to write the program and then to turn it into an executable file.
There are four steps in this process:

a. Write and edit the program.

b. Compile the program.

c. Link the program with the required library modules (normally done automatically).

d. Execute the program. From our point of view, executing the program is one step.
From the computer point of view, however, it is two sub steps: load the program and
run the program.

Write and Edit Programs

The software used to write programs is known as a text editor. A text editor helps us enter,
change, and store character data. Depending on the editor on our system, we could use it for
writing letters, creating reports, or writing programs. The big difference between the other
forms of text processing and writing programs is that programs are oriented around lines of
code, while most text processing is oriented around characters and paragraphs.

The text editor could be a generalized word processor, but it is more often a special editor
provided by the company that supplies the compiler. Some of the features we look for in an
editor are search commands that are used to locate and replace statements, copy-and paste
commands that can be used to copy or move statements from one part of a program to
another, formatting commands that use colors to display key parts of the program, and
automatic formatting that aligns and indents parts of the program.

After we complete a program, we save our file to disk. This file then becomes the input to the
compiler; it is known as a source file.
Compile Programs

The information in a source file stored on disk must be translated into machine language so the
computer can understand it. This is the job of the compiler.

Link Programs

As we will see later in the text, a program is made up of many functions. Some of these
functions are written by us and are part of our source program. However, there are other
functions, such as input/output processes and mathematical library functions, that exist
elsewhere and must be attached to our program. The linker assembles the system functions
and ours into the executable file.

Execute Program

Once the program has been linked, it is ready for execution. To execute a program, we use an
operating system command, such as run, to load the program into main memory and execute it.
Getting the program into memory is the function of an operating system program known as the
loader. It locates the executable program and reads it into memory. When everything is ready,
control is given to the program, and it begins execution.

In a typical program execution, the program reads data for processing, either from the user or
from a file. After the program processes the data, it prepares the output. Data output can be
written to the user’s monitor or to a file. When the program is finished, it tells the operating
system, which removes the program from memory.

TESTING

After we write the program, we must test it. Program testing can be a very tedious and time
consuming part of program development. As the programmer, we are responsible for
completely testing it. We must make sure that every instruction and every possible situation
have been tested.

Designing Test Data

Test data should be developed throughout the design and development of a program. As we
design the program, we create test cases to verify the design. These test cases then become
part of the test data after we write the program.

In addition, as we design the program, we ask ourselves what situations, especially unusual
situations, we need to test, and then we make a note of them. For example, in FindLargest,
what if only one number is input? Similarly, what if the data were in sequence or all the same?
When we design the program, we review it with an eye toward test cases and make additional
notes of the cases needed. Finally, while we code the program, we make more notes of test
cases.

When it comes time to construct the test cases, we review our notes and organize them into
logical sets. Except for very simple student programs, one set of test data never completely
validates a
program. For large-scale development projects, 20, 30, or even more test cases may need to be
run to validate a program. All these test cases become what is known as a test plan.

Finally, as we test the program, we discover more test cases. Again, we write them down and
incorporate them into the test plan. When the program is finished and in production, we still
need the test plan for modifications to the program. Testing of modifications is known as
regression testing and should start with the test plan developed when we wrote the program.
How do we know when our program is completely tested? In reality, there is no way to know
for sure, but there are a few things we can do to help the odds. While some of these concepts
will not be clear until we get to later chapters, we include them here for completeness.

A. Verify that every line of code has been executed at least once. Fortunately, there are
programming tools on the market today that help us do this.

B. Verify that every conditional statement in the program has executed both the true
and false branches, even if one of them is null.

C. For every condition that has a range, make sure the tests include the first and last
items in the range, as well as items before the first and after the last. The most common
mistakes in range tests occur at the extremes of the range.

D. If error conditions are being checked, make sure all error logic is tested. This may
require a temporary modification to the program to force the errors; for instance, an
input/output error usually cannot be created—it must be simulated.

Program Errors

There are three general classifications of errors: specification errors, code errors, and logic

errors. Specification Errors

Specification errors occur when the problem definition is either incorrectly stated or
misinterpreted. Specification errors should be caught when we review our design with analysts
and users.

Code Errors

Code errors usually generate a compiler error message. These errors are the easiest to correct.
Some code errors generate what is known as a warning message, which usually means that the
compiler has made an assumption about the code and needs to have it verified. It may be right,
or it may be wrong. Even though the program may run with a warning message, the code should
be changed so that all warning messages are eliminated.
Logic Errors

The most difficult errors to find and correct are logic errors. Examples of logic errors are division
by zero or forgetting to store the first number in largest in FindLargest. They can be corrected
only by thorough testing. And remember, before we run a test case, we should know what the
correct answer is. Do not assume that the computer’s answer is correct; if there’s a logic error,
the answer will be wrong.

You might also like