📄 chap02.htm
字号:
<font color=#0000ff>const</font> <font color=#0000ff>int</font> daysInMonth[][13] = {
{0,31,28,31,30,31,30,31,31,30,31,30,31},
{0,31,29,31,30,31,30,31,31,30,31,30,31}};
<font color=#0000ff>inline</font> <font color=#0000ff>bool</font> isleap(<font color=#0000ff>int</font> y) {
<font color=#0000ff>return</font> y%4 == 0 && y%100 != 0 || y%400 == 0;
}
}
Date::Date() {
<font color=#009900>// Get current date</font>
time_t tval = time(0);
<font color=#0000ff>struct</font> tm *now = localtime(&tval);
year = now->tm_year + 1900;
month = now->tm_mon + 1;
day = now->tm_mday;
}
Date::Date(<font color=#0000ff>int</font> yr, <font color=#0000ff>int</font> mon, <font color=#0000ff>int</font> dy) {
assert(1 <= mon && mon <= 12);
assert(1 <= dy &&
dy <= daysInMonth[isleap(year)][mon]);
year = yr;
month = mon;
day = dy;
}
Date::Date(<font color=#0000ff>const</font> std::string& s) {
<font color=#009900>// Assume YYYYMMDD format</font>
istringstream is(s);
is >> setw(4) >> year;
is >> setw(2) >> month;
is >> setw(2) >> day;
}
<font color=#0000ff>int</font> Date::getYear() <font color=#0000ff>const</font> {
<font color=#0000ff>return</font> year;
}
<font color=#0000ff>int</font> Date::getMonth() <font color=#0000ff>const</font> {
<font color=#0000ff>return</font> month;
}
<font color=#0000ff>int</font> Date::getDay() <font color=#0000ff>const</font> {
<font color=#0000ff>return</font> day;
}
string Date::toString() <font color=#0000ff>const</font> {
ostringstream os;
os << setw(4) << year
<< setw(2) << month
<< setw(2) << day;
<font color=#0000ff>return</font> os.str();
}
<font color=#0000ff>int</font> Date::compare(<font color=#0000ff>const</font> Date& d2) <font color=#0000ff>const</font> {
<font color=#0000ff>int</font> result = year - d2.year;
<font color=#0000ff>if</font> (result == 0) {
result = month - d2.month;
<font color=#0000ff>if</font> (result == 0)
result = day - d2.day;
}
<font color=#0000ff>return</font> result;
}
<font color=#0000ff>int</font> Date::daysInPrevMonth(<font color=#0000ff>int</font> year, <font color=#0000ff>int</font> month) {
<font color=#0000ff>if</font> (month == 1) {
--year;
month = 12;
}
<font color=#0000ff>else</font>
--month;
<font color=#0000ff>return</font> daysInMonth[isleap(year)][month];
}
<font color=#0000ff>bool</font> <font color=#0000ff>operator</font><(<font color=#0000ff>const</font> Date& d1, <font color=#0000ff>const</font> Date& d2) {
<font color=#0000ff>return</font> d1.compare(d2) < 0;
}
<font color=#0000ff>bool</font> <font color=#0000ff>operator</font><=(<font color=#0000ff>const</font> Date& d1, <font color=#0000ff>const</font> Date& d2) {
<font color=#0000ff>return</font> d1.compare(d2) <= 0;
}
<font color=#0000ff>bool</font> <font color=#0000ff>operator</font>>(<font color=#0000ff>const</font> Date& d1, <font color=#0000ff>const</font> Date& d2) {
<font color=#0000ff>return</font> d1.compare(d2) >= 0;
}
<font color=#0000ff>bool</font> <font color=#0000ff>operator</font>>=(<font color=#0000ff>const</font> Date& d1, <font color=#0000ff>const</font> Date& d2) {
<font color=#0000ff>return</font> d1.compare(d2) >= 0;
}
<font color=#0000ff>bool</font> <font color=#0000ff>operator</font>==(<font color=#0000ff>const</font> Date& d1, <font color=#0000ff>const</font> Date& d2) {
<font color=#0000ff>return</font> d1.compare(d2) == 0;
}
<font color=#0000ff>bool</font> <font color=#0000ff>operator</font>!=(<font color=#0000ff>const</font> Date& d1, <font color=#0000ff>const</font> Date& d2) {
<font color=#0000ff>return</font> d1.compare(d2) != 0;
}
Duration
duration(<font color=#0000ff>const</font> Date& date1, <font color=#0000ff>const</font> Date& date2) {
<font color=#0000ff>int</font> y1 = date1.year;
<font color=#0000ff>int</font> y2 = date2.year;
<font color=#0000ff>int</font> m1 = date1.month;
<font color=#0000ff>int</font> m2 = date2.month;
<font color=#0000ff>int</font> d1 = date1.day;
<font color=#0000ff>int</font> d2 = date2.day;
<font color=#009900>// Compute the compare</font>
<font color=#0000ff>int</font> order = date1.compare(date2);
<font color=#0000ff>if</font> (order == 0)
<font color=#0000ff>return</font> Duration(0,0,0);
<font color=#0000ff>else</font> <font color=#0000ff>if</font> (order > 0) {
<font color=#009900>// Make date1 precede date2 locally</font>
<font color=#0000ff>using</font> std::swap;
swap(y1, y2);
swap(m1, m2);
swap(d1, d2);
}
<font color=#0000ff>int</font> years = y2 - y1;
<font color=#0000ff>int</font> months = m2 - m1;
<font color=#0000ff>int</font> days = d2 - d1;
assert(years > 0 ||
years == 0 && months > 0 ||
years == 0 && months == 0 && days > 0);
<font color=#009900>// Do the obvious corrections (must adjust days</font>
<font color=#009900>// before months!) - This is a loop in case the</font>
<font color=#009900>// previous month is February, and days < -28.</font>
<font color=#0000ff>int</font> lastMonth = m2;
<font color=#0000ff>int</font> lastYear = y2;
<font color=#0000ff>while</font> (days < 0) {
<font color=#009900>// Borrow from month</font>
assert(months > 0);
days += Date::daysInPrevMonth(
lastYear, lastMonth--);
--months;
}
<font color=#0000ff>if</font> (months < 0) {
<font color=#009900>// Borrow from year</font>
assert(years > 0);
months += 12;
--years;
}
<font color=#0000ff>return</font> Duration(years, months, days);
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can now write tests for the functions
you want to implement first, something like the following:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C02:SimpleDateTest.cpp</font>
<font color=#009900>//{L} Date</font>
#include <font color=#004488>"Date.h"</font>
#include <iostream>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>int</font> nPass = 0, nFail = 0;
<font color=#0000ff>void</font> test(<font color=#0000ff>bool</font> t) {
<font color=#0000ff>if</font>(t) nPass++; <font color=#0000ff>else</font> nFail++;
}
<font color=#0000ff>int</font> main() {
Date mybday(1951, 10, 1);
test(mybday.getYear() == 1951);
test(mybday.getMonth() == 10);
test(mybday.getDay() == 1);
cout << <font color=#004488>"Passed: "</font> << nPass << <font color=#004488>", Failed: "</font>
<< nFail << endl;
}
<font color=#009900>/* Output:
Passed: 3, Failed: 0
*/</font> <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In this trivial case, the function
<B>test( )</B> maintains the global variables <B>nPass</B> and
<B>nFail</B>. The only visual inspection you do is to read the final score. If a
test failed, then a more sophisticated <B>test( )</B> would print out an
appropriate message. The framework described below has such a
<B>test( )</B> function, among other things.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">As you continue in the test-and-code
cycle you'll want to build a suite of tests that are always available to keep
all your related classes in good shape through any future maintenance. As
requirements change, you add or modify tests
accordingly.</FONT><A NAME="_Toc519041907"></A><BR></P></DIV>
<A NAME="Heading65"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H3 ALIGN="LEFT">
The TestSuite Framework</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">As you learn more about XP you'll
discover that there are some automated unit test tools available for download,
such as JUnit for Java and CppUnit for C++. These are brilliantly designed and
implemented, but I want something even simpler. I want something that I can not
only easily use but also understand internally and even tweak if necessary. And
I can live without a GUI. So, in the spirit of
TheSimplestThingThatCouldPossiblyWork, I present the <I>TestSuite Framework</I>,
as I call it, which consists of two classes: <B>Test</B> and <B>Suite</B>. You
derive from <B>Test</B> (an abstract class) to override the <B>run( )</B>
method, which should in turn call <B>test_</B> for each Boolean test condition
you define. For the <B>Date</B> class above you could do something like the
following:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C02:DateTest.h</font>
#include <font color=#004488>"..</font><font color=#004488>/TestSuite</font><font color=#004488>/Test.h"</font>
#include <font color=#004488>"Date.h"</font>
<font color=#0000ff>class</font> DateTest : <font color=#0000ff>public</font> Test {
Date mybday;
Date today;
<font color=#0000ff>public</font>:
DateTest()
: mybday(1951, 10,1) {}
<font color=#0000ff>void</font> run() {
testOps();
testDuration();
}
<font color=#0000ff>void</font> testOps() {
test_(mybday < today);
test_(mybday <= today);
test_(mybday != today);
test_(mybday == mybday);
}
<font color=#0000ff>void</font> testDuration() {
Date d2(2001, 7, 4);
Duration dur = duration(mybday, d2);
test_(dur.years == 49);
test_(dur.months == 9);
test_(dur.days == 3);
}
}; <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can now run the test very easily,
like this:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C02:DateTest.cpp</font>
<font color=#009900>// Automated Testing (with a Framework)</font>
<font color=#009900>//{L} Date ../TestSuite/Test</font>
#include <iostream>
#include <font color=#004488>"DateTest.h"</font>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>int</font> main() {
DateTest test;
test.run();
test.report();
<font color=#0000ff>return</font> test.getNumFailed();
}
<font color=#009900>/* Output:
Passed: 7, Failed: 0
*/</font> <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">As development continues on the
<B>Date</B> class, you'll add other tests called from
<B>DateTest::run( )</B>, and then execute the main program to see if they
all pass.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>Test</B> class uses RTTI to get
the name of your class (e.g., <B>DateTest</B>) for the
report</FONT><A NAME="fnB7" HREF="#fn7">[7]</A><FONT FACE="Georgia">. The
<B>setStream( )</B> method lets you specify where the output will go, and
report sends output to that stream. <B>Test</B> is implemented later in this
section. </FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In addition to <B>test_</B>, the
framework includes the functions <B>succeed_</B> and <B>fail_</B>, for cases
where a Boolean test won't do. For example, a simple <B>Stack</B> class template
might look like this:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C02:Stack.h</font>
#include <cassert>
#include <cstddef>
#include <stdexcept>
#include <string>
#include <<font color=#0000ff>new</font>>
<font color=#0000ff>using</font> std::logic_error;
<font color=#0000ff>using</font> std::string;
<font color=#0000ff>using</font> std::bad_alloc;
<font color=#009900>// MS std namespace work-around</font>
#ifndef _MSC_VER
<font color=#0000ff>using</font> std::size_t;
#endif
<font color=#0000ff>class</font> StackError : <font color=#0000ff>public</font> logic_error {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -