Thursday, February 12, 2004

Re: Assertions!!

I briefly skimmed through the article. He makes some good points, but I disagree with him regarding where to use assertions.

are you trying to imply that assertions should be used only for debugging?

Yes...

Personally, here is how I view things. There are three types of "customers" of your code (depending on what you are developing). First, is the end user of your product. Second, is a third party developer who will use (program against) your code. And finally, third is YOU or your team.

If you are developing a library or component, then most probably you will NOT have an end user. A third party developer will be your customer. He will be using your library in his app. And conversely if you are developing a full application, you will not have a third party guy programming against it - unless it is something like MS Word which has that capability.

Anyway, I feel that assertions are to be used only for YOU and your team. Neither the end user nor the third party programmer should face assertion faliures. They should instead face exceptions. For the end user, you should display a nice user friendly message and I guess log it. For the third party developer, just throw the exception (which should be in the documentation). Assertions should only be used during development. Before releasing your code, you should remove them (not literally - there are compiler options where you can tell it to just ignore them).

Assertions are there to protect you from yourself. When you sprinkle assertions all over your code and you run it, if you have made any errors, you will notice it when the assertion fails. So, you do you testing and everything runs fine. You release the product to the customer. One day your app shows error messages or stops responding or whatever. He calls you up and tells you he entered x, y, z value. You enter those values, while in debug mode and run. One of your assertions fails. Now you know where the bug is. You don't want anyone else seeing that. You only want them seeing nice user friendly messages. Same with other developers... they should only have to deal with exceptions your code throws. What exceptions your code throws should be stated in the documentation so they know why it is doing that.

could you provide an example for the above design? would help in understanding much better.

When you write a class, it essentially holds on to some data. Imperitive or OO programming is all about modifying that data or state. So the idea of a class invariant is to assert that all that data is in a valid state at all times.

public class Person
{
    // Data
    private string firstName;

    private string lastName;

    private int age;

    // Methods
    public Person( string firstName, string lastName, int age )
    {
        this.firstName = firstName;

        this.lastName = lastName;

        this.age = age;

        if ( !IsValid() )
        {
            throw InvalidDataException();
        }

    }

    public string FirstName
    {
        get
        {
            Assert( IsValid() );

            return this.firstName;
        }

        set
        {
            this.firstName = value;

            Assert( IsValid() );
        }
    }

    public int Age
    {
        get
        {
            Assert( IsValid() );

            return this.age;
        }

        set
        {
            this.age = value;

            Assert( IsValid() );
        }
    }

    // Class Invariant
    private bool IsValid()
    {
        if ( this.firstName == null || this.firstName.Length == 0 )
        {
            return false;
        }

        if ( this.lastName == null || this.lastName.Length == 0 )
        {
            return false;
        }

        if ( this.age < 1 || this.age > 65 )
        {
            return false;
        }

        return true;
    }

}

This class keeps track of first/last name and age - the state. I've specified that I want the person to have a first name and last name and the age should be between 1 and 65. That's what the class invariant ( IsValid() ) is checking. In the body of "getter" methods (methods that DON'T modify the data), before doing anything, I check that the object is in a valid state. Similarly, in the body of "setter" methods (methods that DO modify the state), after doing everything, I check that the object is in a valid state.

Exceptions have the potential of getting caught in some generic try/catch block. You wouldn't know about it. The good thing about assertions is that you cannot ignore them.

No comments: