Program Design Methodology

 

CS 161: Introduction to Computer Science 1

 

 

Program Design Methodology

 • Programming is really all about solving problems...but we use a computer to do that for us! With small problems -- you may be able to get away with just thinking of the solution to the problem in your head.

 

• In fact, many of you probably are writing your algorithms as an after thought instead of using an algorithm as a road-map to actually solving the problem by breaking it down in to major tasks & then subtasks. And not coding until we could get down to very fine detail of those subtasks FIRST WITH WORDS/Pseudo code.

 

• However, as we progress we will be concentrating on solving larger problems where the first step MUST be to design an algorithm to break down the problem in to sub-tasks and then we will implement the subtasks one at a time!

 

• Therefore, in order to solve the problems on a computer -- we must know how to design good algorithms. You all know what a basic algorithm is ... but now we have to concentrate on getting good at it! Because if you do your algorithm wrong (i.e., you go about solving the problem incorrectly)...then you might get the whole program wrong! When dealing with a big project where there may be multiple engineers developing code for one big program -- picking the wrong solution could lead to schedule delays...budget overruns...and overall your bosses lack of confidence in your work!

 

• Once you have an algorithm that REALLY WORKS - then programming is a very simply task - dealing primarily with the syntax we have been learning.

 

• The only problem is that designing algorithms is a very creative process...and might even be considered to be an artform. It is patterned after how we think...so that no two algorithms for a complex program will be alike!

 

• None the less - there are some basic steps we should be very familiar with when solving a problem and writing an algorithm. There is no guarantee that by following these steps you will create an accurate algorithm ... but it should help you to take the problem a step at a time and break it down into bite size pieces!

 

Steps to Developing an Algorithm

 

First....

• Make sure you have a complete specification. Look at what the specification states...does it make sense? Is it clear? When you read it do you have questions.

 

• Double check that the specification clearly outlines what input into the program is required...and what its format is (is all of the data on one line). Look at what output is required...is it completely defined?

 

• Next look at error conditions. Under what cases does the specification require error messages - and how should they appear. What type of data is correct versus incorrect?  Should the program compute answers even if the data is incorrect? 

 

• Make sure you know how the program should act when it ends. Is there a final message that gets printed to signify that the end of the program has been reached?

 

• Once you've made a list of all of the problems and questions you can come up with the specification -- go to your instructor, supervisor, or project leader and get them ironed out. Either participate with rewriting the specification or request that a new specification be provided that covers all of your concerns.

 

• So - Having a COMPLETE, CONCISE, and CORRECT specification is a NECESSARY first step! Ensuring this will enable you to not leave out vital information in your program.

 

 

Next....

• Formulate a precise statement of the problem to be solved by the algorithm.

 

• Break the problem into subproblems (subtasks).

 

• For each subtask...

            ... formulate a precise statement of the problem the subtask is to solve

            ... use existing algorithms if they have already been developed

            ... Or, use standard techniques for solving the problem if they exist

            ... design the data structure necessary to organize the data involved

            ... write out each step of the subtask in english/pseudo code. These

                        steps should be at such a low level that it is clear how to                                    develop a C++ program to do each step. The solution at this

                        point for each step you write out should be very obvious.

 

Then....

• When implementing the C++ code -- the modules (functions) designed should match the major subtasks developed!

 

• When a problem is divided into subtasks --- you can design algorithms for each subtask -- and therefore code and debug each subtask separately. This way we can test/debug a portion of the entire problem a step at a time to make sure that we are correctly solving each step.

 

• This approach is called procedural abstraction - where we build functions that match our major subtasks -- which can become self-contained subpieces. Once they are designed and debugged - we no longer have to worry about their inner-workings and can just be concerned that we know what their (a) purpose is, (b) input parameters are, and (c) output parameters are.

 

Now Let's Look at an Example --- Let's design an algorithm:

 

1. What if our problem specification was:

      Write a modular program that maintains a database of bibliographic references in the form Author, Title, Journal, Volume, Page, Year. There may be 40 characters in the Author's name, in the book Title, and in the Journal's name. The Volume, Page, and Year are all integers. Design a program that allows users to enter a list of references, view the list, search for a desired reference by author or title, and delete references.

 

2. So, our first step is to make sure our specification covers everything we need to know inorder to design an algorithm:

      • What about error checking?

      • How does the user indicate what task he wants to do?

      • Does this program run forever or is there a way to terminate it?

      • What type of message should be printed at the end of the program?

      • When responding to a request to view the list -- how should it appear?

      • How many references can be in our database?

 

3. Once we get answers to these questions, it is time to start designing our algorithm. Let's first precisely state what the program will do:

      Maintain a database of bibliographic references which can be interactively modified (references added, deleted), viewed, or searched.

 

4. The major tasks would be:

      1. Introduce the user to our interactive bibliographic references program

      2. Find out what the user wants to do: Enter a reference, View all references, View a specific reference,  Delete a reference, or quit.

      3. Either ...

            a. Enter a reference,

            b. View all references,

            c. View a specific reference,

            d. Delete a reference, or

            e. If the user wants to quit, provide a shut down message and thank                   him for using our system!

      4. Unless the user wants to quit -- continue with Step #2.

 

5. Now let's break this down into subtask...

6. And, design the data structures

 

• Once the algorithm is designed...it is time to test it. Choose test data that will expose any possible errors. Use boundary values -- using the largest and smallest possible values allowable. Use erroneous data and see if the correct results occur. Double check that all of your loops iterate the appropriate number of times. Make sure that all cases are fully exercised. A good rule of thumb is to design (YES DESIGN!) your tests to execute every line of code in addition to checking the above conditions.

 

 

• After we fully test and fix our bugs (called debugging), we should look at our code and verify that it is indeed portable.  Since programs represent a large investment in programming time, it really does pay to make sure it will work on a variety of computers. One way this is done is to use standard C++ syntax and not tricks that may be available on one system but not on another.  This doesn't ensure portability, but it is a step in the right direction.

 

 

• This means we should not assume that our system automatically initializes our variables to zeros or blanks. We should ALWAYS explicitly initialize our variables.

 

• When you do need to use a feature that is depending on a particular computer, try to isolate it in a particular module so that when you have to maintain the program, or port it to another system, you can easily see what portions have to be touched. It is always a good idea to treat input/output using this approach.

 

Software Engineering

• In this class are programs are very small in comparison to the development of most industry software systems. In industry - a large amount of planing needs to be done before any code is written down. Plus - the software isn't DONE when you think you are finished debugging. Customers need to take a look at the code (maybe called alpha/beta release) and see if it really meets their needs. Then the code needs to be officially released, maintained, and then will evolve as customer's needs change and develop.

 

• This development process is actually called the software life cycle. It has phases of:

            Specification

            Design

            Implementation

            Testing

            Alpha/Beta Release

            Revision/Testing/Debugging

            Release of Software & Documentation

            Maintenance

            Evolution

            Obsolescence

 

 

• The first step is to decide what exactly the system is supposed to do. This is resolved with the development of the specification. To create the specification customer's need to be contacted and market research may need to be performed to find out what is needed/desired/wished. Some of these things may be doable -- others may not be. Customer's may only have a vague idea of what they want done -- so asking the right questions is important...they may know little about computers and/or software!

 

• To develop a specification, you need to find out what the input/output is, what task is required to be performed, what error conditions will/can occur, what type of documentation is required (e.g., a user's manual?), How fast should the system be? How often will it need to be changed? etc.

 

• So, the specification for a software system indicates WHAT the system will do. The design of the system indicates HOW it will do it. This stage may be similar to our algorithms!

 

 

Introduce Structure Charts

• A structure chart can be considered a hierarchy diagram.

 

• Let's first take a look at a hierarchy diagram; it represents the breakdown of the algorithm into five main tasks. The tasks are imagined as executing in order from left to right:

 

     Problem: Design an algorithm that reports the first position of a given pattern string within a given text string. If no pattern is found print out an error message. Before we design the algorithm - let's be clear how the input and output should appear:

 

     For example, the prompts and input might be:

                  Pattern: ful

                  Text: Wonderful

 

     The output might be:

                  Pattern found at position 7

 

     Or, or example, the prompts and input might be:

                  Pattern: full

                  Text: Wonderful

 

     The output might be:

                  Error: The pattern was not found in the text string.


 

• Given this, now we are ready to start out algorithm:

 

     Step 1: Announce the purpose of the program

     Step 2: Prompt (ask) for, read, & echo the pattern for which to search

     Step 3: Prompt (ask) for, read, & echo the text string to be searched

     Step 4: Search the given text for the first occurrence of the given pattern

     Step 5: Display the results and/or error message

 

• These five tasks could be shown in the form of a hierarchy diagram: (called the first stage)

 

 • Then, we break down each of the tasks into subtasks. For example, Task 2 might be:

     2a. Prompt for the pattern for which to search

     2b. Read the pattern from the keyboard

     2c. Echo the pattern to the user

     2d. Find out if this is the correct pattern...if not, read in a new pattern (2b)

 

 

• So, the second stage - breaking our subtasks down one more level would create a more complete hierarchy diagram....this evolves as we begin to plan the flow of data from one part of the diagram to another.

 

• Then...let's show the data flow for each of these blocks:

 

 

• Using this approach, the overall problem is attacked first by breaking it down into smaller problems. These smaller problems are dealt with later, by breaking them down into still smaller ones. This process continues until the remaining problems are trivial

 

• Hierarchy means that we use a layered structure, where items are ranking one above the other. For each of the layers you could consider using functions

 

• Look at the next task: task 4. This is where the pattern is searched. As we did before - let's break it down starting at the top. In order to get started, think about how you would search the text for the pattern if you were doing it in your head. You would look through the text, checking each character position for the beginning of an embedded copy of the full pattern. Your outline might look like:

 

 

     4a. check for the pattern at the first character position of the text;

           if the entire pattern is found, starting at that position,

                 then quit searching and report the pattern is found at the

                 first position

           otherwise, continue with step 4b.

 

     4b. check for the pattern at the next character position of the text;

           if the entire pattern is found, starting at that position,

                 then quit searching and report the pattern is found at that                   position

           otherwise, continue with step 4b.

 

      (Continue for each character position)

 

• Since this is a repetitive process, we might do better by describing it as:

 

     4. For each character position in the text string:

         a. check for the pattern,

              if the entire pattern is found, starting at that position,

                 then quit searching and report the pattern is found at that       position

           otherwise, advance to the next position and repeat step 4a

         b. If the entire text string was searched and the pattern was not found,

             report an error.

 

• This is close, but not complete. It doesn't include the detail necessary to check for the presence of the pattern at a particular position. So, we need to break this problem down even further. We need to check for the presence of a pattern by checking for each of its characters in order. Of course, if there are fewer characters remaining in the text beyond the current position than there are characters in the pattern, the pattern cannot be in the text at that position.

 

• Given these thoughts, now let's design the algorithm for task 4 (its more complicated than you thought - huh?):

 

     Function for checking for the pattern at a particular position in the text string:

 

     a. If there are fewer remaining characters in the text than there are in the pattern, ...

             ... then report that the pattern was not found at the current                                position

 

     b. Otherwise, for each character in the pattern...

             ... compare the pattern character to the corresponding text                               character

             ... if it is not identical, return that the pattern was not found at the

                 current position

             ... Otherwise, continue checking

        

     c. If all characters were found to be identical...

             ... report that the pattern was found at the starting position

         Otherwise report that it was not found

 

 

 

 

 

 

 


 

With these steps...we add the following box to our diagram: