Fog Creek Software
Discussion Board




Object State

I can't believe I haven't encountered this before, but my head seems stuck on lousy solutions.

I have always been in the habit of believing (perhaps this belief is wrong, I'm questioning it now) that it is a good thing to keep each object in a valid state, and to check that at the beginning (and in non-const functions at the end too) of each member.

In a familiar MFC idiom:-

class X : public CObject
{
public:
    void FuncA( void )
    {
      ASSERT_VALID(this) ;
      // do stuff
      ASSERT_VALID(this) ;
    }

    int FuncB( void ) const
    {
      ASSERT_VALID(this) ;
      // do stuff
      return something ;
    }
} ;


Now - what about 2 stage Init???

Suppose I need to do InitA() + InitB(), or InitA() + InitC()

At the end of InitA(), and the beginning of InitB() or InitC(), the object almost by definition would not be in a valid state.

I just ran into this in a real program, and have had to comment out lots of ASSERTs (as the Init functions themselves called various helper functions) to make it run in debug mode.

Where has my reasoning gone haywire?

S. Tanna
Friday, June 20, 2003

Your reasoning is off because you only enforce contracts at the public interface.

The "in a valid state" assertions are part of what's called design-by-contract. The "contract" specifies what state the object is in before you call a public method, and what state it'll be in after the method is over. There's a lot more to design by contract, btw, but this will suffice for now.

Anyway, the problem you're having is that you should NOT do the Asserts if you're only calling internal helper functions. It is assumed that the object will, internally, need to go through several invalid states during the processing of a method. As long as it ends up in the right state, it doesn't matter how it gets there.

If you need to use these helper functions both from the outside and internally, you should consider splitting the helper method into two functions - the public version that does the assertion, and an internal version called by the helper which does the real work. That way you still get the contract enforcement at the right place but you don't have to worry about calls from inside the class.

Chris Tavares
Friday, June 20, 2003

Amazing... an interesting discussion that doesn't fall under the following topic:

* whining about unemployment
* complaining about joel's regular contradictions
* pointless banter about why PERL is so great

Guy Incognito
Saturday, June 21, 2003

Perhaps your reasoning has gone haywire in the assumption that you need to initialize your object in two stages.

I mean, by splitting it up like that you're just BEGGING for someone to call InitB and InitC without ever calling InitA.  Document it all you like -- someday, somewhere, someone's gonna make that mistake.

Can't InitB and InitC simply call the common initializer InitA privately? 

Alyosha`
Saturday, June 21, 2003

I agree with what has been stated so far but I thought I'd toss in another possibility.  Perhaps your definition of validity is too strict?  For example, "disconnected" is a valid state for a socket.

SomeBody
Saturday, June 21, 2003

inside your class you have probably some indication for
object state.

something like
  int m_state; // 0 after construct
                      // 1 after init A
                      // 2 after init B

then AssertValid can do a switch on m_state and check the relevant members.

(just redefine Valid Object as 'Valid for it's current state'.)

Michael Moser
Saturday, June 21, 2003

*  Recent Topics

*  Fog Creek Home