Top-down Programming and Stubs

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.