
- •Introduction
- •Saving Time with This Book
- •Conventions Used in This Book
- •Part II: Working with the Pre-Processor
- •Part III: Types
- •Part IV: Classes
- •Part V: Arrays and Templates
- •Part VI: Input and Output
- •Part VII: Using the Built-in Functionality
- •Part VIII: Utilities
- •Part IX: Debugging C++ Applications
- •Part X: The Scary (or Fun!) Stuff
- •Icons Used in This Book
- •Creating and Implementing an Encapsulated Class
- •Creating a Mailing-List Application
- •Testing the Mailing-List Application
- •Customizing a Class with Polymorphism
- •Testing the Virtual Function Code
- •Why Do the Destructors Work?
- •Delayed Construction
- •The cDate Class
- •Testing the cDate Class
- •Creating the Header File
- •Testing the Header File
- •The Assert Problem
- •Fixing the Assert Problem
- •Using the const Construct
- •Identifying the Errors
- •Fixing the Errors
- •Fixing What Went Wrong with the Macro
- •Using Macros Appropriately
- •Using the sizeof Function
- •Evaluating the Results
- •Using sizeof with Pointers
- •Implementing the Range Class
- •Testing the Range Class
- •Creating the Matrix Class
- •Matrix Operations
- •Multiplying a Matrix by a Scalar Value
- •Multiplying a Matrix by Scalar Values, Take 2
- •Testing the Matrix Class
- •Implementing the Enumeration Class
- •Testing the Enumeration Class
- •Implementing Structures
- •Interpreting the Output
- •Defining Constants
- •Testing the Constant Application
- •Using the const Keyword
- •Illustrating Scope
- •Interpreting the Output
- •Using Casts
- •Addressing the Compiler Problems
- •Testing the Changes
- •Implementing Member-Function Pointers
- •Updating Your Code with Member-Function Pointers
- •Testing the Member Pointer Code
- •Customizing Functions We Wrote Ourselves
- •Testing the Default Code
- •Fixing the Problem
- •Testing the Complete Class
- •Implementing Virtual Inheritance
- •Correcting the Code
- •Rules for Creating Overloaded Operators
- •Using Conversion Operators
- •Using Overloaded Operators
- •Testing the MyString Class
- •Rules for Implementing new and delete Handlers
- •Overloading new and delete Handlers
- •Testing the Memory Allocation Tracker
- •Implementing Properties
- •Testing the Property Class
- •Implementing Data Validation with Classes
- •Testing Your SSN Validator Class
- •Creating the Date Class
- •Testing the Date Class
- •Some Final Thoughts on the Date Class
- •Creating a Factory Class
- •Testing the Factory
- •Enhancing the Manager Class
- •Implementing Mix-In Classes
- •Testing the Template Classes
- •Implementing Function Templates
- •Creating Method Templates
- •Using the Vector Class
- •Creating the String Array Class
- •Working with Vector Algorithms
- •Creating an Array of Heterogeneous Objects
- •Creating the Column Class
- •Creating the Row Class
- •Creating the Spreadsheet Class
- •Testing Your Spreadsheet
- •Working with Streams
- •Testing the File-Reading Code
- •Creating the Test File
- •Reading Delimited Files
- •Testing the Code
- •Creating the XML Writer
- •Testing the XML Writer
- •Creating the Configuration-File Class
- •Setting Up Your Test File
- •Building the Language Files
- •Creating an Input Text File
- •Reading the International File
- •Testing the String Reader
- •Creating a Translator Class
- •Testing the Translator Class
- •Creating a Virtual File Class
- •Testing the Virtual File Class
- •Using the auto_ptr Class
- •Creating a Memory Safe Buffer Class
- •Throwing and Logging Exceptions
- •Dealing with Unhandled Exceptions
- •Re-throwing Exceptions
- •Creating the Wildcard Matching Class
- •Testing the Wildcard Matching Class
- •Creating the URL Codec Class
- •Testing the URL Codec Class
- •Testing the Rot13 Algorithm
- •Testing the XOR Algorithm
- •Implementing the transform Function to Convert Strings
- •Testing the String Conversions
- •Implementing the Serialization Interface
- •Creating the Buffer Class
- •Testing the Buffer Class
- •Creating the Multiple-Search-Path Class
- •Testing the Multiple-Search-Path Class
- •Testing the Flow Trace System
- •The assert Macro
- •Logging
- •Testing the Logger Class
- •Design by Contract
- •Adding Logging to the Application
- •Making Functions Inline
- •Avoiding Temporary Objects
- •Passing Objects by Reference
- •Choosing Initialization Instead of Assignment
- •Learning How Code Operates
- •Testing the Properties Class
- •Creating the Locking Mechanism
- •Testing the Locking Mechanism
- •Testing the File-Guardian Class
- •Implementing the Complex Class
- •Creating the Conversion Code
- •Testing the Conversion Code
- •A Sample Program
- •Componentizing
- •Restructuring
- •Specialization
- •Index

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.