Sunday, June 8, 2014

Untangle code with small classes

If you want to simplify code, build small classes.

I have written (for different systems) classes for things such as ZIP Codes, account numbers, weights, years, year-month combinations, and file names.

These are small, simple classes, usually equipped with a constructor, comparison operators, and a "to string" operator. Sometimes they have other operators. For example, the YearMonth class has next_month() and previous_month() functions.

Why create a class for something as simple? After all, a year can easily be represented by an int (or an unsigned int, if you prefer). A file name can be held in a string. Why have a separate class for them?

Small classes provide a number of benefits.

Check for validity The constructor can check for the validity of the contents. With the proper checks in place, you know that every instance of the class is a valid instance. With primitive types (such as a string to hold a ZIP Code), you are never sure.

Consolidate redundant code A class can hold the logic that is duplicated in the main code. The Year class can tell if a year is a leap year, instead of repeating if (year % 4 == 0) in the code. It is easier (and more readable) to have code say if (year.is_leap_year()).

Consistent operations Our Year class performs the proper calculation for leap years (not the simple one listed above). Using the Year class for all instances of a year means that the calculations for leap year are consistent (and correct).

Clear names for operations Our Year class has operations named next_year() and previous_year() which give clear meaning to the operations year + 1 and year - 1.

Limit operations Custom classes provide the operations you specify and no others. The standard library provides classes with lots of operations, some of which may be inappropriate for your needs.

Add operations Our YearMonth class has operations next_month() and previous_month(), operations which are not supplied in the standard library's Date class. (Yes, one can add a TimeSpan object, provided one gets the right number of days in the TimeSpan, but the code is more complex.) Also, our YearMonth class can calculate the quarter of the year, something we need for our computations.

Prevent accidental use An object of a specific class cannot be used accidentally. If passed to a function or class, the target must be ready to accept the class. Our Year class cannot be carelessly passed to another function. If we stored our years in ints, those ints could be passed to any function that expected an int.

These benefits simplify the main code. Custom classes for small data elements let you ensure that objects are complete and internally consistent. They let you consolidate logic into a single place. They let you tailor the operations to your needs. They prevent accidental use or assignment.

Simplifying the main code means that the main code becomes, well, simpler. By moving low-level operations to low-level classes, your "mainline" code focusses on higher-level concepts. You spend less of your time worrying about low-level things and more of your time thinking about high-level (that is, application-level) ideas.

If you want to simplify code, build small classes.

No comments: