AP Computer Science :: Lessons :: The Waterfall Model
Pages 221 - 234
Fundamentals of Java
Pages 19 - 22
During the 1940s and 1950s computers were programmed using punch cards that represented a series of 0s and 1s. A computer understands those 0s and 1s as machine language, and each computer has its own unique language. Coding in machine language is extremely slow and error-prone and it can be nearly impossible to read code written by someone else in machine language. These are just a few of the reasons machine language is no longer used.
Starting in the 1950s assembly language came about to replace machine language. Assembly language uses symbols to represent instructions and data where each assembly instruction corresponds to one machine language instruction. A program written in assembly is translated into machine language by a program known as an assembler. The new machine language program is than loaded and run by a program called a loader. Programs written in assembly are more programmer friendly because they are easier to read, but it can be tedious to modify and is still dependent on the machine since different computers have their own assembly code.
High-level languages are designed to be human friendly so they are easy to read, write, and understand. BASIC, C, Java, and C# are all examples of high-level languages. Every instruction in a high-level language corresponds to many instructions in machine language. A compiler translates a program into machine language, but a program must be recompiled on different platforms. Java is a notable exception to this rule.
The Java compiler translates Java code into a psuedomachine language known as Java byte code. A Java virtual machine is used to run byte code so a virtual machine is necessary to run Java code. Some operating systems, like Mac OS X, come with a built in JVM. A JVM is also known as an interpreter because it pretends to be a computer running programs. This is allows the same Java code to run on different types of computers since it is the virtual machine that needs to be modified for different hardware.
Writing a program in Java is just like writing in any natural language. Programming seems stricter because there are rules you cannot break, but these rules are no different than grammar rules. The difference is you can break grammar rules and still be understood. Breaking syntax rules in programming results in a syntax error.
When you set out to write a program it is important that you first analyze the program request to determine what it will need to do. The next step is to design your program so you will know how it will accomplish its task. You then implement the program by writing it. Finally, a larger program may need to go through an integration step where its multiple parts need to be combined. The maintenance step of a program is the most time-intensive since a program may need to be maintained for a number of years. There are a number of different generations of programming languages that can make the task of creating a program easier. This model of analysis, design, implementation, testing, and maintenance is known as the waterfall model of software development.
The first step of the waterfall model is analyzing the program specification. It is important to make sure you understand a program's specifications (or a class project's instructions). Anything you don't understand should be clarified with the customer (or teacher).
The design step of the waterfall model can save programming time and help create a robust program that won't give inaccurate answers and won't crash when given invalid data or allow the program to proceed. A good design plan involves determining what classes and methods to create. Classes can be identified by finding the "big-picture" nouns that describe the program's major objects. Ignore pronouns or nouns that refer to the user. Other nouns may refer to variables (attributes) within a class. All the verbs in a program can tell you the possible methods of the classes. Bundling a group of methods and data fields into a class is called encapsulation. Below is an example for designing an object-oriented program.
Specification: A program must create a teacher's grade book. The program should maintain a class list of students for any number of classes in the teacher's schedule. A menu should be provided that allows the teacher to
- Create a new class of students.
- Enter a set of scores for any class.
- Correct any data that's been entered.
- Display the record of any student.
- Calculate the final average and grade for all students in a class.
- Print a class list, with or without grades.
- Add a student, delete a student, or transfer a student to another class.
- Save all the data in a file.
Identifying Classes: The nouns in the specification can be used to identify the classes in the program. The nouns are purple in the specification above. The following nouns will be eliminated:
- program: It refers to the program as a whole.
- teacher: It refers to the user of the program.
- schedule: The schedule information needs to be saved so we will use an external file for it.
- data, record: These are synonymous with student name, scores, grades, etc.
- class: This is synonymous with class list.
That means we may have the following classes:
Identifying Relationships: We will talk about inheritance later in the year, but there are a number of composition relationships. You can identify these relationships when you can say a class has-a for a specific object. For example, the GradeBook has-a Menu and a Student has-a name. The following relationships will determine what additional classes or data fields are necessary.
- The GradeBook has-a Menu
- The ClassList has-a Student
- A Student has-a name, average, grade, scores, etc.
Identifying Behaviors: We finally need to identify required methods for our program. You can do this by examining the verbs in the specification. The verbs are orange in the specification above. The verbs are
- maintain <list>
- provide <menu>
- allow <user>
- create <list>
- enter <scores>
- correct <data>
- display <record>
- calculate <average>
- calculate <grade>
- print <list>
- add <student>
- delete <student>
- transfer <student>
- save <data>
There are some decisions to make with the above behaviors. For example, will a Student display his or her own record or will ClassList display the records of the students? You also need to determine whether the scores are entered through the GradeBook, a ClassList, or a Student. There aren't any right or wrong answers for these choices, but you may change your approach during the next step of the model.
A bottom-up method for developing a program means finding classes that do not have any collaborators, or other classes that are necessary to implement a class' methods. A class without any collaborators is an independent class. After implementing independent classes you would implement classes that only depend on one other class, then two other classes, and so on. Constructors and methods within classes should be implemented and tested one at a time. Helper methods are methods that are written to break long methods into smaller tasks. This process is known as procedural abstraction. Finally, variables should be declared as private to hide them from other classes, a process known as information hiding. You can also write stub methods to stand in the place of your real methods until you have a chance to write them. A stub method is simply a method that hasn't been written yet and simply returns a neutral value.
For our example program we can create the following methods:ClassList
- search: May be a good helper method to search for a given student
It's impossible to test every possibility when going through the testing phase of the waterfall model. For this reason it is important to select a good set of test data. Typical values, endpoint values, and out-of-range values are good choices. It is also important to choose positive and negative numbers as well as invalid data.
As an example, let's say we have a program that must be written to insert a value in the correct position in this sorted list: 2 5 9. The following are some values you should include when testing the program:
- A value less than 2
- A value between 2 and 5
- A value between 5 and 9
- A value greater than 9
- 2, 5, and 9
- A negative value
You can see an example of a unit test by downlading this Sort Example. A unit test is a method by which units of source code are tested for their robustness. The Sort Example includes a unit test class you can run to determine if the Sort class works as expected.
If the program does not run as expected you may have to debug to determine the issue. You can view BlueJ Lesson 2 to learn how to debug using BlueJ.
The most time-consuming step in the development waterfall is the maintenance of the program. One thing that can make it easier is making sure you comment your source code. One convention that you should follow is putting the following information in a comment before every method:
- Description of what the method is supposed to do
- Precondition: Statement indicating what is true before the method runs
- Postcondition: Statement indicating what is true immediately after the method runs
It is also a good idea to put a comment at the top of each class with information about the class, your name, and the date of the last modification to the class.