CS210 Lab: Software Engineering Principles


1. Overview of Software Engineering Principles

Software engineering can be seen as a technology that includes a process, a set of methods, and an assortment of tools, which are used to build computer software while minimizing cost.

A few links

In previous course assignments, you may have used an adhoc method to coding – simply sitting in front of the computer and typing code and testing it until it worked correctly.

This is fine for small projects that do not require maintenance, but for larger more complex projects, we require some guidelines or stages to follow or else code and projects become chaotic.

In particular, planning stages are necessary in the work force when you are part of a team developing a system that contains tens of thousands of lines of code. Without a system of control or organization, lots of money and time may be wasted.

As a solution to chaotic programming, software engineering offers some processes that try to assist and control the coordination of software projects.

The most basic process model for software engineering is the Linear Sequential Model, also known as the "classical life cycle" or the "waterfall model". The following diagram and paragraphs outline this approach.

2. Application: Walking through an Example

(this section is based on the Chapter 1 Case Study in C++ Plus Data Stuctures)

So, given this theory on Software Engineering, how can we put it into practice?

Given the problem: "Write and test a C++ class that represents a fraction."
Where do we start?

Answer: you can go through the stages listed in Section 1. The following sub-sections break-down this problem according to these stages.

2.1 Analysis

From your teacher's description of the problem, first write a complete definition of the problem, including the details of expected inputs and outputs. This definition should tell "what" but not "how".

For instance, the teacher has given the following description:

A fraction is made up of a numerator and a denominator.  Create operations
which enable you to initialize a fraction, get the numerator and denominator,
reduce a fraction to its lowest terms, test whether the fraction is equal to
zero or greater than 1, and convert a fraction greater than or equal to 1 into
its proper form (a whole number and a fracton).
This basically defines the problem. Now, you can specify what happens given examples. The following table summarizes three fraction examples:
fraction initialized/
printed
reduced equal to
zero
Is not
proper
printed
proper form
3/4 numerator=3
denominator=4
numerator=3
denominator=4
no proper 3/4
5/4 numerator=5
denominator=4
numerator=5
denominator=4
no improper 1 1/4
6/8 numerator=6
denominator=8
numerator=3
denominator=4
no proper 3/4

You should also specify the format of the input and output. We will specify that the input is two integers typed after the prompt for "numerator:" and "denominator:" The output is "numerator=# denominator=#", "yes/no","proper/improper", or a fraction value according to the table above.

We should also answer the question of what happens in error situations such as when the user inputs a demoninator=0? Perhaps we could print a message that says "Invalid fraction – zero in denominator"

2.2 Design

The design phase works more with the "Hows".

Many design methods are based on decomposing a problem's solution into modules – a cohesive system subunit that performs a share of the work. Cohesive means that each module should have a single purpose or identity and the module should stick together well. A cohesive module can usually be described by a simple sentence. If you have to use several sentences or one very convoluted sentence to describe your module, it is probably not cohesive.

In addition, each module should exihibit information hiding so that changes within it do not result in changes in the modules that use it.

To help visualize the break-down of the design into modules, a visual tool called Class, Responsibility, and Collaboration (CRC) cards is used. They are used by object-oriented programmers to identify a set of cooperating classes. It is good to know something about these cards because are used throughout your C++ Plus Data Structures textbook.

The CRC cards contain room for the following information about a class:
  1. Class name
  2. Responsibilities of the class – usually represented by verbs and implemented by public functions
  3. Collaborators – other classes or objects that are used in fulfilling the responsibilities
To help create this card we might read the specifications of the software and circle the nouns and underline the verbs.
The nouns become the objects; the verbs become operations.

The card created for our fraction class problem might look something like the following.
The input specific to this problem is in blue font.
Class name: Fraction Type
Superclass:
Subclasses:
Responsibilities Collaborations
Initialize (numerator, denominator) Integers
Return numerator value Integers
Return denominator value Integers
Reduce to lowest terms Integers
Is the fraction zero? Boolean
Is it greater than one? Boolean
Convert to proper fraction Integers
We can now specify some details.

We must make some preconditions for the operations. That is, all fractions involved must be initialized before the member functions are called.

Now, we are ready to code the class, but what about a test driver to ensure that this class works correctly?

We will accept some commands from the user and quit only when "Quit" is typed as keyboard input.
Accepted commands are:

Incorrect input will result in an error message requesting another command.

2.3 Coding

The code is created in C++ and three files are produced: Click here to download this project

2.3 Testing

In the past, you may have tested your programs without any method, haphazardly plugging in data until the program fails. There are, however, methods designed for testing: This leads us to the concept of a "test plan". We need to decide on and document our goals for testing. Do we want to test data coverage or code coverage or a mixture of the two?

Our test plan will document the test cases planned for a program, their purposes, inputs, expected outputs, and criteria for success.

In our fraction project we can test our seven member functions.
For IsZero and IsNotProper, we will have to test the true and false conditions. We will need at least one situation where the numerator is 0, and one situation where the fraction is improper.
We should also test Reduced with one proper and one improper fraction that can be reduced.
To test our initialization, we should also ensure that it will not accept a fraction with a 0 in the denominator.

The following might be a test plan for our fraction project.

Test Plan
Reason for Test Case Input Expected Output
To test true condition
for IsZero
Initialize
0
1
numerator=0, denominator=1
IsZero yes
To test false conditions for
IsZero and IsNotProper
Initialize
3
4
numerator=3, denominator=4
IsZero() no
IsNotProper() proper
To test NumeratorIs and
DenominatorIs
NumeratorIs numerator=3
DenominatorIs denominator=4
To test Initialization with
Denominator=0
Initialize
5
0
error
To test ConvertToProper
and IsNotProper
Initialize
4
3
numerator=4, denominator=3
IsNotProper improper
Convert 1 1/3
To test Reduced Initialize
36
27
numerator=36, denominator=27
Reduced numerator=4, denominator=3
To test Reduced Initialize
25
35
numerator=25, denominator=35
Reduced numerator=5, denominator=7

In fact, when we try it, our input and output will look like the following:

Please Enter a Command: Initialize
numerator: 0
denominator: 1
numerator= 0 denominator= 1
Please Enter a Command: IsZero
yes
Please Enter a Command: Initialize
numerator: 3
denominator: 4
numerator= 3 denominator= 4
Please Enter a Command: IsZero
no
Please Enter a Command: IsNotProper
proper
Please Enter a Command: NumeratorIs
numerator= 3
Please Enter a Command: DenominatorIs
denominator= 4
Please Enter a Command: Initialize
numerator: 5
denominator: 0
Invalid fraction--zero in denominator
Exiting without initializing...

numerator= 3 denominator= 4
Please Enter a Command: Initialize
numerator: 4
denominator: 3
numerator= 4 denominator= 3
Please Enter a Command: IsNotProper
improper
Please Enter a Command: Convert
1 1/3
Please Enter a Command: Initialize
numerator: 36
denominator: 27
numerator= 36 denominator= 27
Please Enter a Command: Reduced
numerator= 4 denominator= 3
Please Enter a Command: Initialize
numerator: 25
denominator: 35
numerator= 25 denominator= 35
Please Enter a Command: Reduced
numerator= 5 denominator= 7
Please Enter a Command: Quit
Testing completed.
Press any key to continue

2.4 Maintenance

Maintenance involves making changes to the functioning program to fix operational error and/or to add to or modify the program's function.

In our example class that we've been looking at, after going through all these steps, we may realize that there are some shortcomings to this code and running program.

For instance, when we ConvertToProper, the "whole" number is lost to the fraction (see the below running).

Please Enter a Command: Initialize
numerator: 8
denominator: 3
numerator= 8 denominator= 3
Please Enter a Command: Convert
2 2/3
Please Enter a Command: NumeratorIs
numerator= 2
Please Enter a Command: DenominatorIs
denominator= 3
Please Enter a Command: Quit
Testing completed.
Press any key to continue
It would be nice to have an additional data member called whole. Then, we could convert between proper and improper.

Another improvement to make is to Reduce the fraction before we ConvertToProper.

This is left as the postlab exercise.


3. Lab Exercise

Part 1

Consider the following description:
A date is made of year, day, and month. Create operations to initialize
a date, get the month, get the day, and get the year. 

Your primary tasks for this exercise are:

  1. Create the CRC card for this class (sheet provided by instructor)

Steps include:

Part 2

  • Develop 3 files containing C++ code:
  • Add an overloaded operator== to the class to compare one date to another

    Steps include:

  • Create the mydate.cpp and mydate.h files according to the class description.

  • Create a main.cpp which initializes two date objects and prints them to the screen. The run of the program might look something like this:
    Today is: 9 20 2011
    Birthday is: 9 20 1983
    Press any key to continue
    

  • In summary, you've analysed, designed, coded, and tested (to a limited extent) this program. Now, you can add the following additional feature (during this program's maintenance phase)

  • Overload the comparison operator (==) to compare today to birthday. If the month and day are the same, print "Happy Birthday". The output will be something like the following:
    Today is: 9 20 2011
    Birthday is: 9 20 1983
    Happy Birthday
    Press any key to continue
    

    4. Postlab Exercises

    For postlab exercices, click here.

    This page last modified by Nova Scheidt:
    Tuesday, 24-Jan-2012 10:30:05 CST
    Accessed     times.

    CS Dept Home Page
    CS Dept Class Files
    CS210 Class Files


    Copyright: Department of Computer Science, University of Regina.