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

Testing the Date Class 159

Now, this is a lot of code to deal with. Not to worry — the code breaks down into three separate pieces:

Initialization code (shown at

1) either

sets or gets our individual member

variables

and initializes them to reasonable defaults.

Validation code (shown at

2) checks to see

whether or not the input data is reasonable,

given the rules and the current settings.

 

Algorithmic code (shown at

3 and

4)

does the actual date manipulation

and

 

calculations.

2. Save the source-code file and close the code editor.

Always break your classes into discrete initialization, validation, and calculation pieces. This saves you time by focusing your efforts on what needs to be done, rather than worrying about how to do it.

3. Compile the test code to make sure that you have all of the code properly entered and correct.

Testing the Date Class

As with any other utility class, after you have the code written for the class, you must be able to provide a test driver for that class. The following steps show you how to create a test driver that illustrates that the code is working properly — and shows other programmers how to use the class in their own applications.

1. In the code editor of your choice, create a new file to hold the code for the test driver.

In this example, the file is named ch27.cpp, although you can use whatever you choose.

2. Type the code from Listing 27-4 into your file.

Better yet, copy the code from the source file on this book’s companion Web site. Change the names of the constants and variables as you choose.

LISTING 27-4: THE DATE TEST DRIVER CODE.

#include <stdio.h> #include “date.h”

void DumpDate( Date& d )

{

printf(“Date:\n”);

printf(“As String: %s\n”, d.AsString() ); printf(“Month: %d\n”, d.Month() ); printf(“Day : %d\n”, d.DayOfMonth() ); printf(“Day of Week: %d\n”, d.DayOfWeek() ); printf(“Year: %d\n”, d.Year() );

printf(“Leap Year: %s\n”, d.isLeapYear() ? “Yes” : “No” ); printf(“Number of days in this month: %d\n”, d.numDaysInMonth() );

}

int main()

{

// Initialized date to no values. Date d1;

(continued)

160 Technique 27: Building a Date Class

LISTING 27-4 (continued)

//Initialize to the end of the year to test edge cases. Date d2(12,31,2004);

//Print out the dates as strings for testing. printf(“D1 as string: %s\n”, d1.AsString() ); printf(“D2 as string: %s\n”, d2.AsString() );

//Test year wrap and the operator +=.

d2 += 1;

6

printf(“D2 as string: %s\n”, d2.AsString() );

//Test backward year wrap and the operator -=. d2 -= 1;

printf(“D2 as string: %s\n”, d2.AsString() );

//Test the assignment operator.

Date d3 = d2;

//Check to see whether the class works properly for

//assigned objects.

d3 -= 10;

printf(“D3 as string: %s\n”, d3.AsString() );

//Validate the day of the week. Date d4 (7,27,2004);

printf(“D4, day of week = %d\n”, d4.DayOfWeek() );

//Test the pieces of the date.

Date d5;

d5.setMonth( 11 ); d5.setDayOfMonth( 31 ); d5.setYear( 2004 ); d5.setFormat( YYYYMMDD );

DumpDate( d5 );

return 0;

}

3. Save the code as a file in your editor and close the code editor.

4. Compile and run the application.

If you have done everything properly and the code is working correctly, you should see output that looks like this:

$ ./a.exe

D1 as string: 01/01/2004

D2 as string: 12/31/2004

D2 as string: 01/01/2005

D2 as string: 12/31/2004

D3 as string: 12/21/2004 D4, day of week = 3 Date:

As String: 2004/12/31

5

7

Some Final Thoughts on the Date Class

161

Month:

12

Day :

31

Day of

Week: 0

Year:

2004

Leap Year: Yes

Number

of days in this month: 31

There are some important things to take away from

this output. First, look at the line marked

 

5 in the

date object

output listing. This line is output for the

 

 

which is defined with the void constructor. As you can see, the object is properly initialized with a valid date. Next, let’s look at the line marked with 6. This line is output after we added one day to the 12/31/2004 date. Obviously, this forces the date to wrap to the next year, which we can verify by looking at the output, showing 01/01/2005. We can also

verify, by looking at a calendar, that the date shown at 7 really does fall on a Tuesday (the 3 in the

output). Finally, we run some simple tests to verify that the number of days in the month is correct for December, that the pieces of the date are parsed properly, and that the leap year calculation is correct.

All of this output data allows us to validate that our class works properly and that the functionality can easily be moved from project to project. This will save us a lot of time, and allow us to design our programs with the date functionality already built.

When you are testing a class, make sure that you exercise all of the functionality in the ways your class is most likely to be used — not just the ways that make sense to you at the time. Our tests verified that the date math, formatting, and accessor methods all worked properly.

Some Final Thoughts on the Date Class

As you can see, our Date class is really very useful. However, it could easily be made more useful. For example, you could allow the user to pass in a string to be parsed into its date components, thus solving a common programming problem. Another possible enhancement would be to initialize the default constructor to be the current date. Finally, it would be nice to have the date strings, such as the month and day names, within the class itself and accessible. This would protect them from access by programmers from outside the class. In addition, it could allow us to read them from a file, or get them from some internal resource, to provide internationalization without forcing the end user to know where the data is stored.

If you store literal string information in a class, make sure that the programmer can replace it from outside the class. This will allow the developers to put in their own descriptions, change the text for internationalization, or just modify the text to fit their needs.

28

Overriding

 

 

Functionality with

Technique

Virtual Methods

 

Save Time By

Using factory patterns

Building a manager class

Testing the manager class

One of the most common “patterns” of software development is the factory pattern. It’s an approach to developing software that works like a factory: You create objects from a single model of a particu-

lar object type, and the model defines what the objects can do. Generally, the way this works is that you create a factory class that allocates, deallocates, and keeps track of a certain base class of objects. This factory class really only understands how to manage the object type that forms a base for all other objects in the class tree. However, through the magic of virtual methods, it is able to manage all of the objects. Let’s take a look at how this works. By creating a single factory, using virtual methods that processes a variety of types of objects, we will save time by not having to reimplement this processing each time we need it.

First, we have a class that manages a given base class of objects — it’s called a factory. Its uses virtual methods to manage objects — that is, to add new objects, remove them, return them to the user, and report on which ones are in use and not in use.

Next, we have a set of derived classes. These override the functionality of the base class by using virtual methods to accomplish different tasks. As an example, consider the idea of a variety of different kinds of classes to read various types of files. We would have a base class, which might be called a

FileProcessor class. Our manager would be a FileProcessorManager class. The manager would create various FileProcessors, based on the file type that was needed, creating them if necessary or returning one that was not currently in use.

When you implement a common base class, set up an object pool to manage the objects based on it. That way you can always keep track easily of how they are created and destroyed.