1. Overview of Software Engineering PrinciplesSoftware 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
- capability maturitity level
- another link on capability maturitity level
- a scientific american article
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.
- System/Information Engineering
- gathering info on a systems level
- considering elements such as interfacing software with hardware, people, and databases
- creating a strategic business plan from the requirements gathered and the options available
- determining what the program must do
- including requirements for both the system and the software
- recording how the program meets the requirements
- focusing on four distinct components: data structure, software architecture, interface representations, and procedural (algorithmic) detail.
- implementing the design into a computer language
- detecting and fixing errors and demonstrating the correctness of the program (according to requirements)
- Maintenance (after delivered and used)
- fixing errors
- adapting to changes such as in program function (enhancements), or in interface (for instance, in the case of a new operating system)
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.
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:
|reduced|| equal to
| Is not
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"
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:
- Class name
- Responsibilities of the class – usually represented by verbs and implemented by public functions
- Collaborators – other classes or objects that are used in fulfilling the responsibilities
The nouns become the objects; the verbs become operations.
The card created for our fraction class problem might look something like the
The input specific to this problem is in blue font.
|Class name: Fraction Type|
|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 will create a class called FractionType
- We will have two data members:
- We will have some member functions:
- Initialize – takes two integer values and stores them in num and denom
- NumeratorIs – returns the value of num
- DenominatorIs – returns the value of denom
- Reduced – checks whether the numerator and denominator have a common factor and, if they do, divides both by the common factor. Stores the result in num and denom.
- IsZero – tests whether the fraction is zero. A fraction is zero if the numerator is zero and the denominator is 1.
- IsNotProper – tests if the numerator is greater than or equal to the denominator.
- ConvertToProper – returns the whole-number part and leaves the remaining part in the fraction.
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:
- "Initialize" – to set the numerator and denominator
- "NumeratorIs" – to print out the numerator.
- "DenominatorIs" – to print out the denominator.
- "Reduced" – to reduce the numerator and denominator and print the results.
- "IsZero" – to print out "yes" or "no"
- "IsNotProper" – to print "proper" or "improper"
- "Convert" – convert to proper form and print
2.3 CodingThe code is created in C++ and three files are produced:
- frac.cpp – contains the implemention of all the FractionType member functions.
- frac.h – contains the definition of the FractionType class.
- fracDr.cpp – contains the code to test the FractionType class.
2.3 TestingIn 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:
- black-box testing. Testing based on data coverage. The
tester must know the interface – its expected inputs and
outputs – but does not need to know the details of what's
going on "inside".
- goal-oriented approach. You test at least one example of each type of input, as well as boundaries and other special cases. For instance, if you have integer input, you should test negative values, zero, and positive values.
- clear-box testing. Testing is based on code coverage.
The tester must look at the lines of code detailing the
functioning of a module.
- statement coverage. Requires that every statement in the program be executed at least once.
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.
|Reason for Test Case||Input||Expected Output|
|To test true condition |
|To test false conditions for |
IsZero and IsNotProper
|To test NumeratorIs and |
| To test Initialization with |
| Initialize |
| To test ConvertToProper|
| Initialize |
|To test Reduced|| Initialize |
|To test Reduced|| Initialize |
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 MaintenanceMaintenance 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 continueIt 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 1Consider 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:
Circle the nouns and underline the verbs in the description.
You can use the nouns to determine your data members and the
verbs to determine your member functions.
Create the CRC card, and show your instructor.
Hint: the date class will have 4 responsibilities.
- mydate.h – contains the class definition
- mydate.cpp – contains the implementation of the member functions
- main.cpp – contains the test driver
Today is: 9 20 2011 Birthday is: 9 20 1983 Press any key to continue
Today is: 9 20 2011 Birthday is: 9 20 1983 Happy Birthday Press any key to continue
4. Postlab ExercisesFor postlab exercices, click here.
Tuesday, 24-Jan-2012 10:30:05 CST
Copyright: Department of Computer Science, University of Regina.