Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ Timesaving Techniques (2005) [eng].pdf
Скачиваний:
65
Добавлен:
16.08.2013
Размер:
8.35 Mб
Скачать

The cDate Class 31

Once upon a time, there was a project being done at a very large company. A software audit showed at least five different routines (functions, macros, and inline code) that computed whether a given year was a leap year. This was pretty surprising — but even more surprising was that of those five routines, three were actually wrong. If a bug occurred while the system was calculating whether the current year was a leap year, did the programmer have any idea where to look to solve the problem? Of course not.

In this example, despite the risk of bugs, you still have to determine whether a given date is valid — and whether the given year is a leap year. Your first two basic tasks are to set appropriate defaults for the date, and make sure you can retrieve all the components of the date. The same approach works for any business-rule encapsulation. First, you have to know the pieces of the puzzle that go into the calculations. That way, anyone looking at the code will know exactly what he or she needs to supply. There should be no “hidden” data unless it’s being retrieved from an external source. The code should be plug-and-play; you should be able to take it from one project to another with minimal changes.

Of course, it’s often impossible to completely remove application code from business rules. But that really shouldn’t be your goal when you’re writing business objects. Instead, you should worry about how those objects are going to be used.

When you separate the support code from the business rule that it supports, you separate the bugs that can occur into two types: physical errors and logical errors. This alone saves time in tracking down problems. A logical error won’t crash a program, but it will cause grief in other ways. A physical error isn’t likely to cause you to incorrectly generate checks for billions, but it will crash your application and annoy your users.

Your object should be portable; it is going to be used in multiple projects to support the “date rule.” You want your dates to be valid, and you want to be able to extract the components of the date in any project

that might need that data. At the same time, you don’t want to give people more than they need, so you aren’t going to bother supporting date math, such as calculations for adding days or years to a given date.

This is another important tip when designing classes for use in C++, whether they are business objects or full-blown application objects. Always keep the code down to a minimum; only include what people need. Do not simply add methods to a class for the sheer joy of adding them. If you bury people in code, they will look for something simpler. There is a common acronym for this in the engineering community, known as “KISS”: Keep It Simple, Stupid.

Always bear the error-handling process in mind when you write reusable objects. Your code is more reusable if it returns error messages instead of throwing exceptions or logging errors to some external source. The reason for this advantage is simple: If you require people to do more than check the return value of a method or function in your code, you force them to do a lot of work that they might not otherwise have to do. People resist doing extra work; they’ll avoid your code and use something simpler. (Once again, the KISS principle in action.)

The cDate Class

In order to best encapsulate all of the date information in your program, it is easiest to create a single class that manages date storage, manipulation, and output. In this section, we create a class to do all of that, and call it cDate (for date class, of course). With a date class, we are removing all of the rules and algorithms for manipulating dates, such as leap year calculations, date math, and day of week calculations, and moving them into a single place. In addition, we move the date storage, such as how the day, month, and year elements are stored, into one area that the user does not need to be concerned about.

32

Technique 5: Separating Rules and Data from Code

1.

In the code editor of your choice, create a new

2. Type the code from Listing 5-1 into your file.

file to hold the code for the implementation of your source file.

Better yet, copy the code from the source file on this book’s companion Web site.

In this example, that file is named ch05.cpp, although you can use whatever you choose.

LISTING 5-1: THE CDATE CLASS

#include <string> #include <stdio.h> #include <time.h>

class cDate

{

private:

int MonthNo; int DayOfMonth; int DayOfWeek; long YearNo;

protected:

void GetTodaysDate()

{

//First, get the data time_t t;

time(&t);

struct tm *tmPtr = localtime(&t);

//Now, store the pieces we care about MonthNo = tmPtr->tm_mon;

YearNo = tmPtr->tm_year + 1900; DayOfMonth = tmPtr->tm_mday; DayOfWeek = tmPtr->tm_wday;

}

int ComputeDayOfTheWeek() // returns day of week

{

int sum_calc;

 

int

cent_off, year_off, month_off, day_off;

int

year_end;

 

year_end = YearNo % 100;

// year in century

//The following calculation calculates offsets for the

//century, year, month, and day to find the name of the

//weekday.

cent_off = ((39 - (YearNo/100)) % 4 ) * 2; year_off = year_end + year_end/4;

The cDate Class 33

if (MonthNo == 1)

// January

{

 

month_off = 0;

 

if (((YearNo%4) == 0) && ((year_end !=0) ||

((YearNo%400) == 0)))

 

year_off--;

// leap year

}

 

else if (MonthNo == 2)

// February

{

 

month_off = 3;

 

if (((YearNo%4) == 0) && ((year_end !=0) ||

((YearNo%400) == 0)))

 

year_off--;

// leap year

}

 

else if ((MonthNo == 3) || (MonthNo == 11))

month_off = 3;

 

else if ((MonthNo == 4) || (MonthNo == 7))

month_off = 6;

 

else if (MonthNo == 5)

// May

month_off = 1;

 

else if (MonthNo == 6)

// June

month_off = 4;

 

else if (MonthNo == 8)

// August

month_off = 2;

 

else if ((MonthNo == 9) || (MonthNo == 12))

month_off = 5;

 

else if (MonthNo == 10)

// October

month_off = 0;

 

day_off = DayOfMonth % 7;

// day offset

sum_calc = (cent_off + year_off + month_off + day_off) % 7;

// Using the calculated number, the remainder gives the day // of the week

sum_calc %= 7; return sum_calc;

}

int MonthDays( int month, long year )

{

if ( month < 0 || month > 11 ) return 0;

int days[]={31,28,31,30,31,30,31,31,30,31,30,31 }; int nDays = days[ month ];

(continued)

34 Technique 5: Separating Rules and Data from Code

LISTING 5-1 (continued)

if ( IsLeapYear( year ) && month == 1) nDays ++;

return nDays;

}

public:

cDate(void)

{

// Get today’s date GetTodaysDate();

}

cDate( int day, int month, long year )

{

if ( IsValidDate( day, month, year ) )

{

MonthNo = month; DayOfMonth = day; YearNo = year;

DayOfWeek = ComputeDayOfTheWeek();

}

}

cDate( const cDate& aCopy )

{

YearNo = aCopy.YearNo; MonthNo = aCopy.MonthNo; DayOfMonth = aCopy.DayOfMonth; DayOfWeek = aCopy.DayOfWeek;

}

 

 

// Accessors

 

 

int Month() { return

MonthNo; };

long Year()

{ return

YearNo; };

int Day()

{ return

DayOfMonth; };

int DayOfTheWeek() {

return DayOfWeek; };

bool IsValidDate(int

day, int month, long year);

bool IsLeapYear( long year );

};

3. In your code editor, add the code in Listing 5-2 to the source-code file for your application. Alternatively, you could create a new file called date.cpp to store all of this information separately.

These are the non-inline methods for the class. You can put them in the same file as your original source code, or create a new source file and add them to it.