views:

96

answers:

2

Sometimes you just have a list of operations that need to be performed in a set order, like when implementing a sequence diagram. What are the best ways to enforce code execution order, to prevent refactoring introducing subtle bugs through a change of sequence?

Let's assume that existing unit tests would not catch any problems caused by changing the order of the execution of foo() and bar() in the following.

A few of the methods I've seen and used:

  1. Comments (relies on people reading & understanding them):

    // do this
    foo();
    // then this
    bar();

  2. Fluent grammar (to make the code read more like English and discourage wanton refactoring):

    obj
    .Do
    .foo()
    .Then
    .bar();

  3. State variables & balking (too elaborate):

    foo_done=false;
    if(foo()) foo_done=true;
    if(foo_done) bar(); else throw_an_exception;

  4. Grouping logical blocks into functions:

    void foo_bar()
    {
    foo();
    bar();
    }

...and many more too ugly to describe (nesting, events, arrays of function pointers, naming functions Begin(), Middle() and End()...).

Are there any better-engineered patterns for doing this sort of thing?

+1  A: 

I'm confused as to why the order is significant if unit tests wouldn't catch them, and the dependencies aren't explicit in the code; it implies that side effects are a crucial part of the operation of foo() and bar(). In the which case, the best design strategy would be to make these side-effects explicit!

Dave Gamble
So how DO you make the dependencies explicit? Is the only way we have of ensuring order the passing of a test external to the code body, or are there ways to make the sequence explicit?
Eh? I mean in the most literal sense possible:File handle = function_a(filename);number of records = function_b(file handle);data structure = function_c(file handle, number of records);No-one is going to change the order of those...
Dave Gamble
Not all sequences are quite as obvious. What about get_price(); apply_discount(10%); add_tax(); If the discount is applied after the tax is added, there would be problems and only your unit test (and understanding of the business logic) is going to save you. Maybe "sequencing business logic" is the phrasing I'm after.
In that case the unit test will save you.The question concerned when unit tests won't save you and sequence must be enforced.
Dave Gamble
Make the dependencies explicit by hard-coding them. In your example, you would have `add_tax()` call `apply_discount(10%)`, and `apply_discount()` calls `get_price()`. Use lazy evaluation, so if `get_price()` is called before `apply_discount()` calls it, you just return the previously-computed value. This way, you can still call the other methods from elsewhere (if necessary), but you ensure that they get called in the proper sequence when necessary.
TMN
A: 

You write unit tests to check what happened (state, attributes change) after you call a procedure, so I don't think you need to check if some method was called or not.

Also, if you tie your test to some method, later on, if you change your system to use another method with the same effects of foo(), your test will fail even when your system is doing the right things.

mkato