When designing and implementing something, it is generally preferable to start at the most abstract level, and work down from there. For example, when designing and building a house, we prefer to start by deciding what kind of house we want. This in turn helps us determine how many floors, the rooms on each floor, and eventually what kind of building materials we'll need. This is the top-down approach.
The bottom-up approach begins with the materials we have available and determines the higher level features based on what's possible with what we have. For example, if we start by deciding that our house will be made of snow, mud, or straw, this will strongly influence the type of house we end up with.
The top-down approach doesn't prevent us from using pre-existing components at any level of the design or implementation. It merely allows the design to dictate the components, rather than vice-versa.
These principals apply to computer programs as well as to houses. In order to end up with a program that closely matches our needs, it is preferable to begin at the highest level of abstraction (what the program does and how is is used), and let that determine how the more detailed levels are designed and implemented.
In the implementation stage, the top-down approach involves writing the main program first, along with a stub for each subprogram called by main. A stub is simply an empty subprogram which has a complete interface, or signature, but no real code inside. The interface, or signature, refers to the argument list and return value. (These are the only features of a subprogram that other subprograms should be concerned with.)
The program consisting of a complete main calling one or more stubs can be compiled to weed out the syntax errors, and also tested to ensure that the subprogram interfaces work properly. A testable stub will return a value which indicates that all arguments were received. For example, consider the following program:
!----------------------------------------------------------------------- ! Program description: ! Program to print a table of integer powers !----------------------------------------------------------------------- !----------------------------------------------------------------------- ! Modification history: ! Date Name Modification ! 2011-03-23 Jason Bacon Begin !----------------------------------------------------------------------- module subprograms interface function power(base, exponent) real(8) :: power real(8) :: base integer :: exponent end function end interface end module ! Main program body program function_example use subprograms ! Disable implicit declarations (i-n rule) implicit none ! Variable definitions integer :: x ! Statements print *, 'Powers of 2' do x = 0, 10 ! Will not work with 2.0 instead of 2.0d0 print *, '2 ^ ', x, ' = ', power(2.0d0, x) enddo print *, '' print *, 'Powers of 3' do x = 0, 10 ! Will not work with 3.0 instead of 3.0d0 print *, '3 ^ ', x, ' = ', power(3.0d0, x) enddo end program !----------------------------------------------------------------------- ! Description: ! Compute base ** exponent for any non-negative integer exponent ! ! Arguments: ! base: The real base of base ** exponent ! exponment: The non-negative integer exponent ! ! Returns: ! base ** exponent !----------------------------------------------------------------------- !----------------------------------------------------------------------- ! Modification history: ! Date Name Modification ! 2011-03-23 Jason Bacon Begin !----------------------------------------------------------------------- function power(base, exponent) ! Disable implicit declarations (i-n rule) implicit none ! Function type ! Factorials grow beyond the range of a 32-bit integer very quickly ! so we use a larger data type real(8) :: power ! Dummy variables real(8), intent(in) :: base integer, intent(in) :: exponent ! Local variables ! Return a fake return value that indicates successful reception ! of arguments power = base + exponent end function
This program contains a stub for power(), which compiles, executes, and returns a value indicating that the arguments base and exponent were received properly. The code to actually compute the power will be added later when implementing the second level of abstraction. The stub allows the implementation of the main program to be tested before beginning the implementation of the subprograms called by the main program. After testing with the stub, we now know that the main program works properly. If we encounter errors while implementing power() we don't need to look for bugs in the main program, so the debugging process will be much simpler.