Tips for Writing Classes

Remember that a class is not written for a single purpose.  The whole idea behind classes is for them to be reusable in different programs.  If you write a class specifically for solving your immediate problem (such as lab 2), then you will have trouble reusing that class later (such as in labs 3 and 4).  These tips can help you to write more reusable classes.

#1: Do not perform user input in the class. Do all user input outside of the class, and pass variables into the class.  For example:

The Bad Way:

void Foo::setData ()
{
    cout << "Enter data: ";
    cin >> myData;
}

void main ()
{
    Foo aFoo;

    aFoo.setData ();
}

The Good Way:

void Foo::setData (int value)
{
    myData = value;
}

void main ()
{
    Foo aFoo;
    int temp;

    cout << "Enter data: ";
    cin >> temp;

    aFoo.setData (temp);
}

#2: A method in the class should do exactly one logical task.   When you have a function do more than one thing, you limit reusability.  An example is the code from tip #1 that does user input inside the Foo::setData function.   The function is doing two things: user input, and setting myData.  This limits the reusability of Foo::setData.

#3: Don't do output in a function unless that is the only thing the function does.  Doing oiutput inside functions that are also doing other things limits the reusability of the class.  For example:

int Foo::getData () const
{
    cout << "Data is: " << myData << endl;
    return myData;
}

I don't know when I'm writing this class if output is appropriate when getting the value of data or not.  I should not do the output in Foo::getData.  If output is appropriate, I'll do that in main after calling Foo::getData.

#4: Do not have public data members in the class.  You lose control by making your data members public.  Instead, make the data members private and provide get and set functions for each data member.

#5: Unless you have good reasons not to, provide get and set functions for all logical data in the class.  Do not provide get and set functions for data that is only used internal to the class.

#6: Your get functions should be declared as const functions.  In fact, all functions which do not actually change the data in the class should be declared as const functions. 

#7: Provide operator== and operator!=.  These are useful in many circumstances.

#8: If you do not want a method that the compiler writes for you (copy constructor, operator=), put a prototype for the method in the private section of your class.

#9: The behavior of a class should not be significantly different based on the value of a data member indicating a type.  Note that you will not be able to fix this problem until lab 4.  Here's an example:

class Foo
{
public:
    Foo () { type = 0; }

    int getType () const { return type; }
    void setType (int t) { type = t; }
    void setValue (double v) { value = v; }
    double getValue () const
    {
        if (type == 0)
            return value;
        else
    return value * -1;
    }

private:
    int type;
    double value;
};

The behavior of getValue is different based on whether type is 0 or not.  If there are really two types of Foo objects, each with its own behavior, then we should not combine these into a single class.  Instead we should use inheritance and polymorphism to reuse the common code between the classes.