alert_red.gifORIGINAL: 11/22/03; LAST UPDATE:10/12/05alert_red.gif

LEARNING MODULE VI
SOFTWARE DEVELOPMENT
(D&L's "Problem Solving and Algorithm Design)

        In its "most fundamental form", software development is the programming of a computer.   Nicholas Werth, the designer of the Pascal programming language, said that a program consisted of its algorithm (the detailed method of solution) and its data structures (the organization of its data).  "Structured programming" was the disciplined strategy for efficiently developing software in the 70's and 80's.  In the 90's the strategies of structured programming were generalized into a higher level strategy called object-oriented software development (OOSD), often called "object-oriented programming", or object technology (OT).  During all this time, the efficient development of quality software has been the first skill that must be mastered by computer professionals. In the early days of computing (as well as in current university computer science curricula), programming was essentially an individual undertaking. However, as software became more sophisticated, software development evolved into a group enterprise.   Now "computer programming" has given way to "Software Engineering" which incorporates the design, production, and maintenance of software as if it were an engineering project involving a team.  This basic view was the foundation of OT, which has yet to mature, but is definitely the "wave of the present AND the future".  Regardless of the strategy, the essential feature of software development, like all problem solving, is "divide and conqueor", i.e. subdivide overwhelming problems into smaller ones until the small problems can be solved. Because this is an introductory course we will not delve to deeply into the details of software development strategy so our limited goals of this Learning Module VI are to help the student:

  1. understand the CONCEPTS (not just the "terms") of software development
  2. distinguish the two basic software development strategies:
    1. Structured Programming Strategy based on algorithms
    2. Object Oriented strategy based on class architectures
  3. distinguish non standard terms, guidelines... and strategies so that confusion will not result when different terms, guidelines, etc. are encountered.
  4. Goals of D&L, Chapter 6, i.e. the student will be able to:
    1. distinguish programmable tasks, i.e. those that are suitable for programming,
    2. describe an relate problem solving strategies,
    3. analyze and develop simple algorithms,
    4. analyze and develop simple object oriented class architectures, and
    5. identify and discuss the fundamental "threads" common to all types of software development.

NOTE:  If you are an independent learner (not attending the on-campus classes), it is especially important to read the study guide for this LM.  Even though it is virtually impossible to simulate the interactive in-class presentation on this Web site, I do try.  However, I need your help, so read the study guide to try to understand what I am trying to do.  (I'd appreciate your feedback on how to imporve this simulations of the class environment.)

TPQ 1: Rewrite the preceding objectives in terms of personal accomplishments to be attained after finishing the study of this learning module.

The sequence of presentation of the learning module is:

  1. PROBLEM SOLVING
    1. How to Solve Problems, in General (Polya's Approach)
    2. Computer Problem Solving  Generalized Software Development Strategy
    3. Following an Algorithm
    4. Developing an Algorithm
  2. TOP-DOWN DESIGN STRUCTURED ALGORITHM DEVELOPMENT
    1. Generalized strategy for SP (Not in D&L)
    2. General Example 
    3. A Computer Example
    4. Summary of Methodology
    5. Testing the Algorithm
  3. OBJECT-ORIENTED DESIGN DEVELOPMENT
    1. Generalized strategy for OOSD (Not in D&L)
    2. Object Orientation Encapsulation
    3. Relationships between Classes
    4. Object Oriented Design Methodology
    5. General Example
    6. Computer Example
  4. IMPORTANT THREADS
  5. SUMMARY
NOTE: I have drawn a line through "TOP-DOWN DESIGN" because it can be applied to OOSD as well and, therefore, shouldn't be associated only with algorithm development.  I have replaced "DESIGN" with "DEVELOPMENT" because, in most descriptions of software development "design" is a specific step in both algorithm development and OOSD, so it should not be used to describe the overall strategy.

1. PROBLEM SOLVING:

1.1 How to Solve Problems (Polya's Approach)
:

  1. Summary of Polya's approach, given in D&L, to solving math problems.
    1. Understand  the problem, i.e. clarify the
      1. data given
      2. conditions specified, and 
      3. unknowns to be derived.
    2. Develop a plan that connects the given data to the unknown, following the conditions.
      1. consider similar or related problems
      2. consider the use of theorems
      3. divide and conquer, i.e. solve parts of the problem that give intermediate results.
      4. consider alternatives; don't be one dimensional in your approach.
    3. Carry out the plan.
    4. Examine the solution.
  1. To begin problem solving, ask questions in order to gain a clear understanding of the problem.  Questions can be to yourself, to help you think.                                                                                      
  2. Look for familiar tasks or subtasks within the problem, where you can use known procedures or solutions, i.e. "don't reinvent the wheel!"  On the other hand, don't panic if you don't "know" how to solve a problem; it is typical to expect to recognize how to proceed as soon as you read a problem, but that is not problem solving.  Instead of panicing, consider the challenge a fascinating mystery and enjoy the opportunity to be creative and innovative.
  3. "Divide and Conquer" is a powerful approach to simplifying a complex problem. (Modularization)...Note that breaking down large, vague problem into a sequence of understandable subtasks is the opposite of abstraction; in fact, "analysis" is the essence of divide and conquer and its opposite, "systhesis", is a means of abstraction, i.e. merging details into a whole.
  4. D&L's "Applying these strategies" uses a travel planning example to illustrate Polya's problem solving approach.   Rather than repeat this example consider the following SAQ:
SAQ 1: Apply Polya's problem solving approach to (a) converting a number from one base to another, (b) designing a digital circuit, and (c) writing a scientific paper.  (Discuss in class.)
  1. An algorithm is a set of unambiguous, explicit, step-by-step instructions for accomplishing a specific task in finite time.  Algorithms are one of the foundations of  mathematics, and, when used in computer science, algorithms are the fundamental abstraction of structured programming, a disciplined, procedural approach to software development.  
    1. Actually an algorithm is an "idea" that can be represented by a recipe, a flowchart, pseudocode, etc.  When implemented in a computer language an algroithm is a program or software module (procedure, function, method, etc.)
    2. Computer algorithms must be explicit, i.e. one can not assume a computer knows how to do things like you would assume when giving instructions to a human.  To illustrate and emphasize the point, consider TPQ 2.
TPQ 3: Give the algorithm for a robot to make a peanut butter and jelly sandwitch.  (Discuss in class.)
  1. {Not in the text}A Generalization of Polya's Four-Phase Problem Solving "Heuristics" (guidelines based on experience):
TPQ 4: Think of one verb that describes each of the four steps in Polya's HOW TO SOLVE IT outlined in 1.1.A, above, and in D&L, Figure 6.1.  Place each verb in one of the blanks below, before reading further.  Be sure to use YOUR words.  Remember, the first goal of this LM, to UNDERSTAND THE CONCEPTS, instead of memorizing the words used to describe the concepts; these words are inconsistent - at best!
  1. __________(1) the "problem" with the goal of understanding it.  Specify the requirements (often called "requirements analysis"), i.e. determine what input is required and what output is desired.  (Common sense says: know what you start with and know what results you want.)
  2. __________(2) the solution, i.e. the connection between the inputs and the outputs.
    1. search for known solutions
    2. search for shortcuts (solutions to similar problems, solutions parts of the problem, patterns for solutions, tools like theorems, rules, guidelines, etc.)
    3. divide and conquer, i.e.  break the problem into smaller, manageable subtasks that give intermediate results that lead to the final solution.
    4. brainstorm; consider all alternatives; don't become tranfixed with a single approach.
    5. test and debug solution and constituent subtasks.
  3. __________(3) the  __________(2), (planned in the preceding step) in the appropriate environment, e.g. in digital electornics, create the circuit; in architecture, construct the building; in software, write the source code, etc.  Test and debug.
  4. __________(4) the  __________(3), (created in the preceding step), i.e.
    1. fix faults encountered during use
    2. modify features to imporve performance
    3. update facilities to account for changes in the environment.

TPQ 2: Compare (a) general problem solving (e.g. math, physics, etc.) and (b) technical writing to the generalization of Polya's heuristics, given above.

1.2 Top-Down Design Generalized Software Development Strategy:

  1. "Task automation" is more appropriate than "problem solving" when used to describe software development.   D&L indirectly supports this view because the text emphasize that computers, on their own (i.e. ignorring artificial intelligence) can not solve problems, i.e. that the usefulness of a computer comes from the fact that (See , p. 143) "once we have written a solution for the computer, the computer can repeat the solution very quickly and consistently...for different situations and data".  However, I think we can clarify this statement if we replace "written a solution" with "modelled a task", i.e.  once software devevelopers have modelled a task in software we have an automated task which the computer can perform quickly and consistently for any acceptable input - as often as needed."
    1. "Problem solving" is an appropriate description of any "new" task, i.e. a real "problem" is something that has no solution, at least from the point of view of the problem solver.  This is true for math problems, science problems, writing assignments, travel planning, etc. -- any situation when you don't "know" how to proceed and creativity and innovation are required.  There is no point in writing software for a problem that only needs to be solved once!  Software is only useful for problems that need to be solved over and over with different inputs and/or different circumstances.
  2. If we adapt the generalization of Polya's heuristics, from section 1.1.G, above,  we can say, from a very simplified point of view, that virtually all software development can be outlined according to the following phases (usually repeated within themselves):
Generalized Software Development Strategy
    1. Analysis First analyze the task (problem) and specify requirements (the input needed, the output required, and the constraints imposed on our approach).  At the end of this phase you should have a draft of the input and output requirements as well as any other constraints or requirements imposed on the approach.  There are two different analysis strategies; these are explained in Modularization of Software and summarized below:
      1. Structured Programming analysis involves identifying the main task and recognizable subtasks. (To get started, find the verbs in the task statement).
      2. Object -oriented analysis involves identifying the abstract objects that are involved in the task (To get started, find the nouns in the task statement) as well as how they interact.
    2. Design:  Develop the approach/solution b;y; continuing a modularization approact using a "divide & conquer" strategy, testing each conceptual module for correctness.   At the end of this phase you should have a draft of a language independent representation  of your task automation or problem solution. These two design strategies are explained in Modularization of Software and summarized below.
      1. Structured Programming is based on top-down algorithm development where 
        1. modularization is accomplished by breaking up the main algorithm into subalgorrithms , of which there are two kinds:
          1. procedures which do things and/or pass results to the main algorithm via parameters (e.g. the procedure swap (x,y) which has two results) and 
          2. functions which return a single result to the main algorithm via the function name. e.g. maximumOf (x,y).
        2. Algorithm logic is governed structured control structures (sequence, selection, and repetition)
        3. At the end of this phase, your language independent representation could be complete pseudocode, flowchart, Structured Algorithem Chart, etc.
      2. Object oriented Software Development is based on modelling a software architecture where 
        1. modularization is accomplished by specifying:
          1. classes, ("templates" for objects) and specifying their attributes (that determine the state of objects) and operations (that determine the behavior of objects), and
          2. relationships between the classes
        2. object behaviour is specified by algorithms for its operations and is, therefore, based on the rules of sturctured programming (above).
        3. At the end of this phase, your language independent representation could be complete UML class architecture, set of CRC cards, etc.
    3. Implementation: Code the design in a computer language, testing each coded module for correctness.  At the end of this phase, your language dependent representation will be debugged source code (in a specific language, e.g. C++, Java, Assembly language, etc.)  You source code is debugged by testing the execution of your source code where a compiler/interpreter/assembler translates your source code into object code which is run on the computer.
    4. Maintenance: Monitor the performance of the implementation under all encountered circumstances, modifying the design and implementation to account for encountered errors and updated circumstances.
SAQ: (a) Explain the difference between the "return mechanism" of the two programming modules, swap(x,y) and  maximumOf(x,y), used as examples in step b.1.1, above, i.e. explain how their results are passed back to the algorithm in which they are placed.  ( NOTE: this distinction is too often ignored in programming languages, like C based languages, where only one term, "function" is used to describe both kinds of operations, e.g. a nonsensical term "null function" would be used to describe , swap(x,y).(b) Continue consideration of the distinction between "functions" and "procedures" by discussing the difference between the function maximumOf(inputList) and the procedure  extremesOf(inputList, max, min).
  1. Software development is an iterative process, i.e. the simplified strategy outlined above actually is iterative, i.e. individual steps have repeated substeps, especially including testing.  This is illustrated in Figure of COSC 390.
  2. Algorithms are "ideas" which can have several different "representations", e.g. a recipe, instructions for installing hardware, etc.  Computer algorithms can take several different forms:
    1. Pseudocode is a human language representation of an algorithm, using words (as opposed to diagrams).  Pseudocode algorithms are formatted and presented so they can be be "executed" by humans.  See the examples in D&L on pp. 149, 150, and 153-159.  Note that there is no standard syntax (grammar) for pseudocode However I prefer representations that are as concise as possible; they are easier to see and comprehend than verbose statements, e.g. instead of the "Set sum to sum + digit" in the second example of section 2.1.C, I would use "s <- s +d".  (The "<-" is the best the computer text can do for a left-pointing arrow which represents assignment, i.e. add the current values of s and d, then assign that sum to s!)  In fact, I personally do not like or use pseudocode; I greatly prefer visual representations of algorithms, e.g. flowcharts (See item 3 below.); in fact, I have a nonstandard type of flowcharts called "SAC" for "structured algorithm chart"Time and schedule do not permit my showing this to you, but I might present this, outside class for those who are interested.  (See  Structured Algorithm Charts from COSC 390.)   Here are a few guidelines for writing "structured" pseudocode:
      1. An algorithm represented in pseudocode is a sequence on instructions that can be understood by humans, NOT computers. Instructions have two basically different categories which, using the terminology of D&L, are:
        1. Concrete instructions "can" be executed by a computer (If they were rewritten in "source code"; see item b, below.)  A simple example would be that given above, s <- s +d(Note that "<-" represents an assignment in the preceding examples i.e. they are "shorthand" expressions, e.g. c <- c+2 means "set c equal to c+2".)
        2. Abstract instructions are those that can NOT be executed by a computer, i.e. they have no equivalent in the computer language you plan to use.  (You can't write the source code equivalent of an abstract instruction in a computer language, e.g. "FindRoute from start to finish".)  To "complete" the algorithm development phase, all abstract instructions must be converted to concrete instructions, i.e. we must rewrite, using top-down "divide and conquer" strategy, single abstract instructions in terms of subalgorithms (procedures or functions) that contain only concrete instructions.  (Obviously, a computer program will NOT run, if there is only one instruction it does not understand.)
      2. All algorithms and subalgorithms have a name.
      3. Concrete instructions (statements) are a small subset of English that include, but are not limited to:
        1. input/output instructions, e.g. 
          1. input (....) or read (...), where .... represents a constant, variable, or list of these.
          2. output (....) or print (...)
        2. assignment statements, :
          1. mathematical expression assignments, e.g. c <- c+2, s <- s+d, x <- a*b + (c-d)/e;, etc.
          2. boolean assignments, e.g. found <- true
          3. string (text) assignments, e.g.  firstName <- "Tyron" or wholeName <- "John" + " " + "Smith".
          4. others (beyond the scope of the course)...
        3. control structures for:
          1. selection e.g. if-then-else ; see if-then pseudocode, below.
          2. repetiiton, e.g.while loop ; see while loop pseudocode, below.
          3. FORBIDDEN Instructions in "structured" pseudocode include:
            1. "goto", the most "dangeroous" instruction in programming.  (In fact, structured programming could be called "goto-less" programming.
      4. Abstract instructions have two basic kinds:
        1. functions (in OOSD called "methods", but they should be called "functional methods") are subalgorithms that process input and return a single value via the function name.  For example,  sumOfAllIntegersBetween(first, last) would add all the intergers between (an including) first and last; the result would be a single value that could be "returned" to the main program via the name, sumOfAllIntegersBetween , i.e. "sumOfAllIntegersBetween", in the main program, would be like a variable that holds the sum of the integers after it had been calculated.
        2. procedures (in OOSD called "methods", but they should be called "procedural methods"; in C they are called "null functions") are subalgorithms that process input and return multiple values via "parameters" (variables that are "tied" to the procedure where they are used).  For example, swap(first, last) would switch the values of first and last; the results would consist of two values that could NOT be returned via a single name; they would have to be returned via the parameters first and last.
SAQ: Write the algorithms for the (a) function sumOfAllIntegersBetween(first, last) and (b) the procedure swap(first, last).  (c) What is the most basic difference between these two algorithms?
      1. Finished algorithms, ultimately consist completely of concrete statements, i.e. any abstract instruction must have subalgorithms that "somewhere down the line" have nothing but concrete instructions.  Remember an algorithm is no finished until is can be translated
    1. Source code is a computer language representation of an algorithm.   Unlike pseudocode, source code has an intolerant exact syntax for every computer programming language; if you misspell one word or misplace one semicolon, the bloody programming languages refuses to do anything until the source code is perfect.  (Note that although source code is referred to a program, "program" has many other interpretations, e.g. object code is a machine language program.)
    2. Object code is the machine language representation of an algorithm (but humans certainly could not recognize their algroithm in machine language); it can be executed by a computer - the ONLY thing that can be executed, directly, by a computer.   Object code "can" be written by hand, but, normally, software developers write source code and then use a "compiler"  to convert the source code to object code
    3. A flowchart is a schematic representation of an algorithm.
      1. Standard flowcharts are actually based on computer-oriented "branch" instructions.  (See the example flowchart  where the diamonds represent branch instructions.)
      2. Tony's "Structured Algorithm Charts" (SACs) are a flow chart adaptation to high-level, structured programming that consists of sequence, selection, and repetition constructs.  (See the SAC equivalent of the previous flowchart example.)
  1. NOTES:
    1. The last two steps in the preceeding strategy are basically the same as the last two in D&L, Figure 6.2 of the text.  In fact, the first two are subdivisions of the first step in  D&L.   The only thing missing is the word "algorithm".            Obviously  D&L's "problem solving" is algorithm based, i.e. for structured programming NOT object-oriented programming, the newer strategy!
    2. Be sure to note that similar approachs to all types of "problem solving" (even theme writing) as you study the following software development strategies!

1.3 Following an Algorithm:

This section in D&L illustrates how the recipe for Hollandaise sauce expemlifies the "implementation" of an algorithm and how the receipe can be written in pseudocode.

1.4 Developing an Algorithm:

This section in D&L shows how our daily activities are examples of implementing algorithms, i.e. carrying out a sequence of instructions.

2. TOP-DOWN DESIGN STRUCTURED ALGORITHM DEVELOPMENT:

      D&L's terminology implies that the terms "top-down design" and "modules" apply only to algorithm development; however they apply equally well (perhaps better) to object-oriented design.  In OOSD, the top-down "hierarchical" organization applies to the inheritance hierarchy of a software architecture and classes are the software modules.  (See section 3, below.) Therefore, I have treated "top-dow design" and "module" as generic terms and chosen more specific terms like "structured algorithm development" and "subalgorithm" ("procedures" and "functions") to describe the concepts of this section.

2.1 Generalized strategy for SP (Not in D&L):


Compare the specifices of the following phases with those of section 3.1.)
  1. In the structured programming approach to the phases of software development the developer must:
    1. Analyze the Task and specify its Input/Output requirements.
      1. Specify the required input data and its mechanism, e.g. CLI, GUI, files, etc. (In operations/methods these would be arguments.)
      2. Specify the required output information and how it is to be presented.
      3. Specify the operations that process the input into output (Note that this functional/procedural approach is fundamentally different than an object oriented approach).
      4. Identify and name subtasks.
    2. Design the Logic, using a top-down strategy (using, flowcharts, pseudocode, etc.; see section .. .).
      1. Define first level module (the "program")
      2. Refine the design, stepwise, by modulariation (organizing self-contained logic in highly cohesive procedures and functions), until every module is reduced to instructions that can be executed by a computer.  (D&L  call such instructions "concrete steps", i.e. algorithm steps that are explicit, not abstract.)
      3. Test and Debug (trace algorithm with pencil and paper) for a complete set of inputs that cover all algorithm threads.  Correct all design bugs (faults) detected.
      4. At the end of this phase, your language independent representation could be complete pseudocode, flowchart, Structured Algorithem Chart, hierarchy chart, etc.
    3. Implement the logic, top-down, in code using a computer language.
      1. Code, in a specific language, e.g. C++, Java, Assembly language, etc., the program and its modules (procedures and functions) using "stubs" (named procedures or functions that do nothing except say they were called) for untested modules. 
      2. Test and debug each newly coded moduleYour language dependent representation will be debugged source code  of the module (procedure or function) you are developing.  You source code is debugged by testing the execution of your source code where a compiler/interpreter/assembler translates your source code into object code (machine language) which is run on the computer.  The "stubs" of untested modules will be "transparent" to the computer, until they are developed and debugged.
      3. Finally, test and debug the complete program with a complete set of test data.  
      4. Document (This important phase is often omitted, as in D&L, but should not be ignored by the software developer!)
      5. At the end of the implementation phase, your language dependent representation will be debugged source code for the complete program. 
    4. Evaluate and Maintain
      1. After software is deployed, monitor its performance by continual testing and evaluating user feedback.
      2. Update imperfect software by following the preceding phases in redesigning existing modules and adding new modules.
  2. This "programming" is based on algorithm development in the analysis and design phases.  This is illustrated in the most basic of computer algogrithms, the Input-Proces-Output sequence, presented by the following flowchart.
 
INPUT-PROCESS-OUTPUT ALGORITHM
in Flowchart form
IPO Flowchart

The programming cycle is, conceptually, separated into a language independent phase (analysis and design) and a language dependent phase (implementation and testing). This separation is irrelevant to the Documentation phase, but also applies the the maintenance phase, if significant modifications are made to the software.

  1. {NOT IN D&L!} In modern structured and OO programming, logic "control structures" are used to define the logic of an algorithm and govern logic flow of program execution.   ( Note that all of the following applies to the development of OO operations/methods that define the behavior of classes, i.e. structured algorithm development is part of OOSD.)  In fact, it has been proved that any programmable algorithm can be coded using only three different control structures, sequence, selection, and repetition; actually all one needs is the if-then-else for selection and the while loop for repetition; these are the only ones that should be used by COSC101 students.
    1. Sequence: the default order of program execution, one instruction after another.
    2. Selection: this control structure allows the program to choose one of two or more alternative sets of instructions (subtasks). It involves evaluating a "selector", and, depending on its value, deciding which (if any) set of instructions to execute before passing control to the instruction following the selection.
      1. The most fundamental form is the if-then-else construct (also called "if-then-else" or just "if-else") which facilitates selection of one of two, mutually exclusive sets (blocks) of instructionsIt is the "selection" constructor from which all other selection constructs can be created.  An example of an if-then-else is:

IF-THEN-ELSE CONTROL STRUCTURE PSEUDOCODE
if (Grade >= 60) then
       Print "Pass"
else
       Print "Fail"
endif
      1. The case statement provides for more than two selection categories; however, this can be simulated using "nested" if-then-else control structures, i.e. if-then-else within other if-then-else structures.
    1. Repetition: the repetition of a block of instructions may be accomplished two ways:
      1. iteration (or looping), the most common form of repetition in data processing languages, is the cycling through a block of instruction over and over until a condition arises that halts the looping. There are two fundamentally distinct types of loops:
        1. definite loops have a predefined number of repetitions e.g. for Index := 0 to 9 print (Index); (* A Pascal "for loop" *) repeat 4 [forward 50 right 90] ; A Logo "repeat loop"
        2. indefinite loops repeat themselves while a looping condition is true or until an "exit condition" occurs; these loops are indefinite because, unlike definite loops,  it is NOT known, at the beginning of these loops how many repetitions will occur, i.e. something has to happen  within the loop body, to cause the repetitions to end.  It is the "while" constructor from which all other repetition constructs can be created  An example of a pseudo code segment using a "while loop":
WHILE CONTROL STRUCTURE
 PSEUDOCODE

Set sum to 0
Set digit to 1
while (digit <= 9)
     Set sum to sum + digit
     Set digit to digit +1
endWhile


SAQ: (a) What does the preceeding while loop do?   (b) Rewrite the preceding algorithm using "Tony's shorthand" with "<-".
      1. Recursion, the preferred method of repetition in artificial intelligence languages like PROLOG, LISP or Logo, involves a subprogram "calling" itself (i.e. doing its instructions) over and over until a condition arises that halts the repetition.
  1. A powerful strategy for solving algorithmic problems is given in the appendix Problem Solving: Developing Algorithms  which introduces structured algorithm charts (SACs) which are the most powerful, efficient "tool" for helping developers design elegant algorithms, of which I know.
Notes:
  1. the discussion in section C, above, simply means that algorithm instructions are executed sequentially UNLESS this is modified by selection or repetition control structures
  2. Although there are several kinds of the selection and repetition control structures, it has been proved that any programmable task can be performed using only the if-then-else  for selection and while for repetition.  This sounds deceptfully simple, until one realizes that selections an repetitions can be nested, i.e. occur within each other and within them selves.  Anyway, to represent structured algorighms, one must have, at a minimum,  an if-then-else construct and a while loop; this applies to pseudocode or flowcharts, both of which can represent algorithm logic.
  3.  Appendix  Problem Solving: Developing Algorithms  proved and illustrated that there are only two ways to organize control structures, in sequence or nested:
    1. Sequential control structures are independent, i.e. occur one after another.
    2. Nested control structures mean that one control structure "contains" other control structures within them.  Consequently the nested control structures are dependent on those control structures that contain them.

SAQ: Write the pseudocode for an algorithm segment that would count the number of positive numbers, negative numbers, and zeros in a file of integers.  (Although, this might be challenging to true beginners, you have everything in section 2.1.C.)

2.2 General Example  This illustrates top-down algorithm development in a social invironment, e.g. planning a party.

2.3 A Computer Example:
  1. D&L's example is an incomplete treatment of the application of the fundamental top-down algorithm development strategy to a list of names and addresses.
    1. There are mistakes on page 159.
      1. The "Put list in alphabetical order" is a Level 1 module, not Level 3.
      2. The "Print the list" is Level __() module; the text does not give it's level or even put it in the hierarcy diagram at the bottom of p. 159.
    2. The modules are written in terms of:
      1. instructions which have computer equivalents that can be executed by a computer, e.g. read, write, while, and if(D&L call these "concrete steps".)
      2. procedures (named modules), e.g. Get and Sort, that must ultimately be expressed with instructions in order to complete the algorithm.   (D&L call these "abstract steps".)  (Note that "functions" are another kind of module, but these are not illustrated, at this point in D&L.)
    3. The example is unfinished, i.e. it leaves two tasks, Get and Sort, unspecified (abstractions  that can not be executed by a computer).
    4. Typically algorithm developers start by writing algorithms by hand.
    5. Note that all the abstractions (algorithm instructions that are not concrete steps) are "procedures", i.e. they occupy one line of the sequence of instructions.  In C++ procedures are called "null functions", a contridiction in terms.
    6. Note that the "list" of names is a data structure, i.e a collection of data characterized by how the data is accessed, i.e. first in first out.  However, exactly how this list is maintained is ignored in this introductory illustration. (Data structures are discussed in D&L, Chapter 9, so we will defer consideration of how the list is created and maintained until LM IX.)
    7. When writing pseudocode, it is advisable to give the modules meaningful names that are one word because these names can be used for the procedures or functions in the source code.  (Spaces are not normally allowed in module names.)  Therefore, I would rewirte the Level 0 algorithm as:
      1. InputNames
      2. UpdateDataValues
      3. SortList
      4. PrintList
  2. An alternate example (to be developed in class): Rather than rehash the example in D&L, let us develop the pseudocode representation of the algorithm for the following task:  Develop, using a top-down algrothm development strategy, the pseudocode for an electornic "student gradebook", for a single student, that allows grades,  between 0 and 100, to be entered into a list.  First, the algorithm should prompt the user to enter the number of grades to be processed.  The gradebook should automatically calculate the average of the set of grades and then the grades should be normalized to 78.  Finally, the list of normalized grades should be printed.  (Note: you only need to develop the algorithm for one set of grades.) (HINT: be sure to trace (deskcheck) your developing algorithm, often, to check your logic.) (b) After finishing this prototype, refine it by placing an error trap that would "prevent" grades outside the range 0-100 from being entered.  (This will illustrate how modular design allows you to modify a module without affecting the other modules.  Also note how "clumbsy" this algorithm is, i.e. we have to keep typing the grades in each time we use the list; this inefficiency will be removed when we consider the array data data structure in LM IX.)
    1. Analysis (divide and conquer, underlining verb phrases: KISS; get the task out of the words, into you head):
      1. Inputs Given (Use meaningful variable names, abbreviations for conciseness. Draw pictures if possible.):
      2. Outputs required  (Use meaningful variable names, abbreviations for conciseness. Draw pictures if possible.):
      3. Constraints:
    2. Design (design the algorithm logic via divide and conquer using pseudocode, flowchart, SAC, hierarchy chart, etc.):
      1. First level algorithm: list a sequence of named procedures (subalgorithms).  Test and debug.
      2. Design the algorithm of each procedure; new procedures may be declared within the level 1 procedures
      3. Continue designing, top down, the algorithm until everything is written in an instruction that the computer can execute (D&L's "concrete steps").
    3. Implement - This can not be done until we learn how to code algorithms in a computer language.
    4. MaintainThis can not be done until we learn how to code algorithms in a computer language.
NOTES:
  1. The preceeding example is the best that I can think of to illustrate Analysis and Design (for both algorithms and objects).  It contains, at a simple lever, examples of all of the fundamental "constructs" of algorithms (input, output, sequence selection, and repetition) plus useful instructions like counting and accumulating as well as a two dimensional "data structure", the list of names and grades.
  2. We will "attack" the same example using OO Analysis and Design at the end of the next section.
SAQ: The algorithmic analysis involved underlining verbs and verb phrases; OO analysis, coming next, will involve underlining ______ and ______ phrases.  Try this and compare the results.
SAQ: {extensions of electronic gradebook example}...

2.4 Summary of Methodology:  This has already be given in section 2.4.A, above.

2.5 Testing the Algorithm:
  1. Testing occurs at all phases of development,  i.e. during algorithm (and object-oriented) design as well as all levels of implementation (coding in a programming language).
    1. D&L uses three terms to describe testing during the design phase:
      1. "Desk checking" (also called "tracing") is a individual's manually working through an algorithm with pencil and paper.  This is usually the first kind of error checking.
      2. A "walk-through" is a team approach to desk checking.
      3. "Inspection" is another team approach to testing where algorithm instructions are read aloud and team members try to detect imperfections.
    2. Testing  during the implementation phase involves running the software using the simplest set of data that covers all possible circumstances.
  2. Testing occurs at all levels of development
    1. "Top Down Testing" involves testing the higher level modules first, assuming the lower level modules work properly.
    2. "Bottom Up Testing" involves testing and debugging the lowest level modules first, then testing the upper level modules in order until the highest level is debugged.
  3. Every module should be tested separately, i.e. every procedure, function, method, class, etc. should be checked to see that it works correctly on its own.
  4. Care should be taken to use a complete set of test data that covers all conceivable circumstances, for every module.  For a complex software  task, this may be impractical at the design phase, so care should be taken to use an efficient data set for desk checking.  Of course, a computer's speed makes complete testing easier during the implementation phase, but care should still be used in designing a simple set of complete data.
ASSIGNMENT: Do the first part of Lab 6, i.e. instruction 4, as an example of algorithm analysis and design.

3. OBJECT-ORIENTED DESIGN DEVELOPMENT:

     The "structured programming" paradigm, covered in section 2, above,  was based on using structured control constructs() (selection and repetition) for controlling program flow and abstraction() facilities (data types and modular constructs like procedures and functions) for modularizing software.   These were provided in sturctured programming languages like Pascal, C, etc.  Next, the modularization/abstraction trend was extended to "object-based programming" with the introduction of the class construct which allowed modelling of "objects".  The "class" formalized the  encapsulation of state (data declarations called "attributes") and behavior (operation definitions called "methods") into one module, the class; modular programming languages included Modula 2, Ada, etc.  Finally the transition to "object-oriented software development" (OOSD) was completed with the addition of inheritance and polymorphism which minimized redundancies and maximized the human point of view; object-oriented programming languages included Modula 3, Ada 95, C++, Java, etc.  Therefore, we can concisely specify the difference between strutured programming and OOSD by saying OOSD includes all of the the concepts of structued algorithms plus the following.

  1. Encapsulation allows the developer to package everything needed to specify a classifier (modeling element) within the classifier definition.  The most common application is to use classes to encapsulat its members (attributes and operations), but there are others, e.g. packages, interfaces, etc.  In pure OOSD, everything must be encapsulated, i.e. no operations (functions) can be defined outside of a class.
  2. Visibility allows the developer to control the access to the members of a classifier.  This typically involves declaring the attributes or operations of a class to be public, package, protected, or private.
  3. Inheritance allows the developer to derive subclasses from superclasses, thus using the non private member of the superclass in the subclass.  The UML recognizes only two forms of inheritance, 
    1. generalization that results in a subclass being a subtype of the superclass and
    2. realization that causes a class to implement an interface.
  4. Polymorphism allows the developer to use the same name in different contexts in the same software.  Polymorphism is manifested many ways including conversion/coercion of types, overriding/hiding, overloading, abstract operations, polymorphic attributes, polymorphic methods, and polymorphic classes (templates).
Only encapsulation and relationships are discussed in D&L (visibility and polymorphism are ignored), so the following sections only cover these as well.  A complete, but very concise specification of UML class diagrams covering all of the above OO features is given in A SIMPLIFIED, PRACTICAL OVERVIEW OF UML CLASS DIAGRAMS - which is not required for COSC 101.

Work through the Intuitive Introduction to OOSD using UML class diagrams.

3.1 Generalized strategy for OOSD ( Not in D&L)
( Compare the specifices of the following phases with those of section 2.1, above.)
  1. Analyze the Task and specify its abstract objects and their interrelationships (Compare with the Analysis subcycle of section 2.1.)
    1. Identify the abstract objects (distinct, highly-cohesive components) that represent the task domain and the roles of each abstract object.  Possibilities, called "candidate classes", can be identified by distinguishing the nouns in the problem statement (task specifications).   (Good OOS developers will utilize the "uses case" diagrams of the UML or CRC of D&L. to analyze the task being modelled.)
    2. Identify the abstract object members that specify its state (UML attributes) and behavior (UML operations)
    3. Identify the relationships between abstract objects that facilitate a loosely coupled architecture of classes
  2. Design the Software Architecture, using the UML using a top-down strategy.  (Compare with the Design subcycle of section 2.1.)
    1. develop a model that implements the specifications from the Analyze/Specify phase
    2. Define first level
      1. modules i.e. classes for each abstract object.    The candidate classes, identified in A.a, above, are tentative, i.e. they look OK at first analysis, but they may be replaced or even discarded as the model is refined.  (Modules also include interfaces and packages, but these advanced constructs will be ignored at our introductory level.)
      2. relationships between modules.  These are less obvious than candidate classes, but some like the UserInterface class will always "use" other classes, when it instantiates them (creates real objects from the abstract objects, i.e. classes).
    3. Refine, stepwise, the architecture;
      1. declare attributesIf the attribute type is a class (i.e. NOT a built-in language primative), then the declaration of an attribute class specifies an association ("has-a") relationship between the classes.
      2. define methods (UML operations) that represent behavior. If the type of an argument, return type, or local variable type is a class (i.e. NOT a built-in language primative), then their declaration specifies a dependency ("has-a") relationship between the classes.
      3. declare interclass relationships that minimize redundancy (efficiently reuse classes and their elements), e.g. inheritance.
      4. identify and minimize redundancies by defining new classes and collecting the redundancies
    4. Deskcheck (trace) and Debug
    5. At the end of this phase, (1) your language independent representation of the software architecture could be complete UML class architecture, complete set of CRC cards, etc.  and (2)your language independent representation the class operations could be complete pseudocodes, flowcharts, Structured Algorithem Charts, hierarchy charts, etc. for each nontrivial operation.
  3. Implement the architecture, top-down, in a computer language.