Stepwise Refinement

Shame on you all that I had to add this page on 7th March 2003!

The Wiki Is Nota Dictionary crowd probably prevented it earlier.

Stepwise Refinement is a relatively old technique of Software Design that has been successfully used in a wide range of Structured Programming and Modular Programming environments and languages. It is the procedural (step-by-step) form of Separation Of Concerns and has what some may call a fractal nature of task division.

The principle of Stepwise Refinement kind of tries to roll-up You Arent Gonna Need It and Keep It Simple in one and most programmers (at least the Good Programmers I've encountered) tend to use Stepwise Refinement intuitively.

The software design is approached as being a series of layers of modules of decreasing abstraction with call flows typically forming hierarchies through the modules. You start by identifying the top of the hierarchy (essentially main() or do_stuff()) and then apply Top Down design to work out the next set of modules that need to be built/written.

Stepwise Refinement can (and often is) also applied even down to the function level in languages such as Cee Language.

For example, the top level might be the function main(). The Software Engineer decides that main needs to call foo() and bar(), so she writes the function main() to call foo() and bar() but leaves foo() and bar() stubbed out with printf's. She then runs her Unit Tests and confirms that foo() and bar() are called correctly i.e. we get printfs coming out where expected, so main() works. She then implements foo() which is straightforward, and runs the Unit Tests once more to verify that both main() and foo() operate together correctly. She then goes back to look at bar() but realizes that bar() needs another lower level function, baz() to work. So, she implements bar() to call baz() but again just leaves baz() stubbed out. Once more the Unit Tests are run to confirm that the software works correctly. The final stage is then to implement baz() and finished checking the software with the Unit Tests.

The advantage of Stepwise Refinement is that it allows for Incremental Development but on a much finer level of granularity. A little bit like Barry Boehm's Spiral Model. It also uses Unit Tests as an integral feature of the development process. The software is also rapidly built as Stepwise Refinement lends itself naturally to producing working (and tested) prototypes of the software as it develops, and it is often possible to build prototypes in remarkably short periods of time as you can apply YAGNI pretty much down to the function level. Stepwise Refinement is highly scalable, as large systems can be developed in a structured and predictable fashion from it.

The downside is that Stepwise Refinement is open to interpretation of precisely what abstraction functions are required at the higher levels. This generates a tendency towards an architecture that has one larger high-level module with several smaller "worker" modules below it. That is, the hierarchy tends to grow across rather than down (which is the intention).

I'd be interested to hear anyone else's thoughts on this. Having been programming Cee Language for a few years now (after being a Java Language and Object Oriented person for 2 years) I have become quite fond of Stepwise Refinement and really do wonder how people program without it, especially when using Structured Programming languages.

If you feel the need, Please Comment.


Example:

Brush Teeth

find toothbrush

find toothpaste tube

open toothpaste tube

Put thumb and pointer finger on cap

turn fingers counter-clockwise

repeat prior step until cap falls off

squeeze tube onto toothbrush

(details omitted)

clean teeth

put brush on teeth

move back and fourth vigorously

repeat above step 100 times

clean up

rinse brush

turn on water

put head of brush under running water for 30 seconds

turn off water

put cap back on toothpaste

put all items back in cabinet


Unfortunately, Stepwise Refinement often leads to a solution where each module represents one part of the task in chronological terms, which can lead to multiple modules knowing the details of some data structures. See David Parnas' wonderful paper On Decomposing Systems for examples of different ways to decompose a system, some of which are more robust against changes in data representation.

I don't see wrapping data structures and Stepwise Refinement to be mutually exclusive. David Parnas paper is flawed in some ways.

They aren't, but it takes care to do both at the same time. The point is that it's very easy to think of the "steps" in Stepwise Refinement as being "steps to solve the problem", which tend to be chronological. For example, if the above "brushing your teeth" system were implemented naively, it would be natural to have subroutines for each line, called by the next higher level and calling the ones at the next lower level, all of which have knowledge of the "toothbrush" and "toothpaste" data structures.

Please explain "which have knowledge"? Perhaps this is related to Database Not More Global Than Classes.

As far as subroutines, yes in practice one often does such. The above illustrates the design dividing process, not the coding and repetition factoring.


Stepwise Refinement and Deviation Management

"Dealing with Deviations from Framework" under Helpers Instead Of Wrappers illustrates how to use the levels to go lower into the Stepwise Refinement tree to "override" behavior for custom exceptions-to-the-rule, but still potentially use some of the existing branches. -t


See original on c2.com