You are on page 1of 225

<

.
*

^
WUi4jn-\\c-li'y

Q.

$&
;.'

, |

!: ,
~* !:|

Kent Beck

Addison-Wesley
An imprint of Addison Wesley Longman, Inc.
Reading, Masachusetts Harlow, England Menlo Park, California
Berkley, California Don Mills, Ontario Sydney
Bonn Amsterdam Tokyo Mexico City

'*,

? ,-* -

18
-^

*;<\

hi

*"?

< Stn

r l

^*
-
--

2003

:


.



.
.
.
.
.
.

32.973.2-018
681.3.06
42

.
: .
. .: , 2003. 224 : .
ISBN 5-8046-0051-6
, , ,
.
? , ,
. TDD (Test-Driven-Development , ). ?
. TDD ,
.
, TDD.
TDD,
, TDD. ,

.

Pearson Education, Inc., 2003


, , 2003
, , , 2003
Addison-Wesley Longman.
.
.
, , , .
, ,
, , .

ISBN 5-8046-0051-6
ISBN 0-321-14653-0 (.)
, 196105, -, . , . 67.
05784 07.09.01.
005-93, 2; 95 3005 .
14.08.03. 70x100/16. . . . 18,06. 3000. 297

,
198005, -, , 29

.10
.11

15

16

17

I.

21

1.

22

2.

29

3.

32

4.

36

5.

39

6. ,

.42

7.

47

8.

49

9.

53

10. times()

58

11. . .

63

12. , -

66

13.

71

14.

76

15.

81

16. , -!

85

17.
?

89
89
90

JUnit

91
92
92

93
.

95

I I . xUnit

97

18. xUnit

98

19. ( setup)

104

20. ( tearDown)

108

21.

112

22.

115

23.

118

24. xUnit

123

I I I .

125

25. ,

126

126

(Isolated Test)
(Test List)
(Test First)
assert (Assert First) .
(Test Data)
(Evident Data)

128
129
131
132
134
135

26.
One Step Test ( )
Starter Test ( )
Explanation Test ( )
Learning Test ( )
Another Test ( )
Regression Test ( )
Break ()
Do over ( )
Cheap Desk, Nice Chair ( , )

136
136
137
139
139
140
141
141
143
144

27.
(Child Test)
Mock Object ( )
Self Shunt ()
Log String (-)
Crush Test Dummy ( )

145
145
146
147
149
149


Broken Test ( ) . . .
Clean Check-in ( )

7
151
151

28.
Fake It ()
Triangulate ()
Obvious Implementation ( )
One to Many ( )

153
153
155
156
157

29. xUnit
Assertion
Fixture ()
External Fixture ( )
Test Method ( )
Exception Test ( )
All Tests ( )

159
159
161
163
164
166
166

30.
Command ()
Value Object (-)
Null Object (-)
Template Method ( )
Pluggable Object ( )
Pluggable Selector ( )
Factory Method ( )
Imposter ()
Composite ()
,
Collecting Parameter ( )
Singleton ()

168
170
170
172
173
175
176
178
179
180
182
183

31.
Reconcile Differences ( )
Isolate Change ( )
Migrate Data ( )
Extract Method ( )
Inline Method ( )
Extract Interface ( ) . . .
Move Method ( )
Method Object ( )
Add Parameter ( )
Method Parameter to Constructor Parameter (
)
32. TDD
?
?
?

. 184
184
185
186
188
189
190
191
192
193
193
195
195
196
196

TDD ?
?
?

TDD?
TDD
?

?
TDD
?
TDD?
TDD ?
TDD ?
TDD ?
?
TDD
?
TDD

197
198
200
201
201
202
203
204
205
205
206
208
208
210

211

I.

213
214

215

I I .

217

220


, (clean code that works), ,
, (Ron Jeffries),
Test-Driven Development (TDD). ,
, , , .
. ,
,
.
, .
, ,
, .

.
,
.
.
, ?
, ,
. ,
, .
, Test Driven Development (TDD).
:
, ;
.
, ?
:
,
, , ;
,
, - ;

;

, ,
.

11

TDD
.
1. , , ,
.
2. ,
.
, .
3. .
TDD.
, , ,
,
, , ,
. , , ,
, :
,
(Quality Assurance, QA)
() ;
,
,
;
,
,
;
, ,

,
.
, , ?
,
? ,
?
.

TDD .
.
, , ,
, . : !,
: !
,
:

12

,'
;
;
;
.

, . ,
,
,
;
,
;
, ,
,
;
( .)
.
, ,
. , ,
. ,
, .
, ,
, . ,
.
TDD . ,
, , .
, , .
, , . .
, ,
.
Extreme Programming Explained
(Extreme Programming) TDD (Test-Driven
Development). TDD .
: , ,
. TDD . TDD
,
,
. ,
, ,
"? TDD? , TDD.
,
.

13

, TDD, ,
. (test infected)
(Erich Gamma) ,
. TDD, ,
, , ,
. ,
, TDD,
, TDD ,
.
, , ( ,
) . , TDD

. ,
, ,
.
, ,
, .
, :
;
;
,
.
.
I. . ,
TDD.
(Ward Cunningham) ,
TDD. :
,
. ,
, ,
.
II. xUnit. ,
Reflection API .
.
xUnit,
.
,
.
III. .
, ,
, xUnit.
,
, .

14

,
. , ,
,
,
. ,
, ,
,
. ,
, , ,
, .
. ,
,
. ,
(
). ,
. , ,
, . ,
,
, ,
, ,
.
. TDD .


,
.
,
. , ,
: (Steve Freeman), (Frank
Westphall), (Ron Jeffries), (Dirk Koning),
(Edward Heiatt), (Tammo Freese), (Jim Newkirk), (Johannes Link), (Manfred Lange),
(Steve Hayes), (Alan Francis), (Jona
than Rasmusson), (Shane Clauson), (Simon Crase),
(Kay Pantecost), (Murrey Bishop),
(Ryan King), (Bill Wake), (Edmund Schweppe),
(Kevin Lawrence), (John Carter), (Phlip),
(Peter Hansen), (Ben Schroeder), (Alex Chaf
fee), (Peter van Rooijen), (Rick Kawala),
(Mark van Hamersveld), (Doug Swartz),
(Laurent Bossavit), (Ilia Preuz), (Daniel Le Berre),
(Frank Carver), (Mike Clark),
(Christian Pekeler), (Karl Scotland), (Carl Manaster), . . (J. . Rainsberger), (Peter Lindberg), (Darach Ennis), (Kyle Cordes), (Justin
Sampson), (Patrik Logan), (Darren Hobbs),
(Aaron Sansone), (Syver Enstad), (Shinobu Kawai), (Erik Meade), (Patrik Logan), (Dan Rawsthorne), (Bill Rutiser), (Eric Her
man), (Paul Chisholm), (Asim Jalis),
(Ivan Moor), (Levi Purvis), (Rick Mugridge),
(Antony Adachi), (Nigel Thome), (John Bley),
(Kari Hoijarvi), (Manuel Amago), Kaopy (Kaouru Hosokawa), (Pat Eyler), (Ross Shaw),
(Sam Gentle), Qean Rajotte), (Phillipe Antras) (Jaime Nino).
. ,
.
, ,
TDD. ,
. ,

16

(Massimo Arnoldi), (Ralph


Beatti), (Ron Jeffries), (Martin Fowler)
(, ) (Erich Gamma),
, ,
.
(Martin Fowler)
FrameMaker.
( ,
, ).

(Ward Kunningham). , ,
, ,
, ,
Smalltalk Smalltalk. He
,
. ,
, .
:
, ,
. ,
, .
,
, , ,
.
(Mike Henderson) ,
(Marcy Barns) , .
, , 12-
. :
, ,
. , , .


, ,
comp@piter.com ( , ).
!
web- http://www.piter.com
.


(Ward Cunningham)
, WyCash.
,
.
, . :
. , ,
. ,
. :
?
, . ,
, . ,
,
, , .
WyCash
.
, . ,
,
(Guaranteed Investment Contracts),
.
WyCash - ,
.
Dollar, .

.

Dollar. ,
Smalltalk ,
.
, , ,
,
, .

, Dollar.
, 1,
\
1

c2.com/doc/oopsla91.html.

18

. ,
, Dollar ...
,
, .
, . ,
, , ,
, AveragedColumn.
.

, , , .
.

, .
,
.
,
: , ? ,
, .
Dollar
.
, . 2
43 - 15 USD 200 CHF1.
,
Dollar (), Currency (). ,
,
PolyCurrency ().
, ,
, . , ?
Currency ( ) ;
. .
, .
.
WyCash ,
. , -
.
:
WyCash
, ,
.
USD , CHF . . .

19


WyCash,
( , , ).
,
; ,

.
, ; ,
-
. , ,
. ,
. ,
(
, , ),
?
, . ,
,
, . Test-Driven Development (
), TDD ,
, ,
, . , .
. ,
,
:
,
, ;
.
,

, . ,
, (multi-currency
money).

,
( ,
).
(Test-Driven
Development, TDD). , TDD
:
1. .
2. , .
3. .
4. ,
.
5. (refactoring)
.
, :
,
?
, ,
?
?

?

, WyCash,
(. ). , .

IBM
GE


1000
400

25
100

25 000
40 000
. 65 000

, .

IBM
Novartis


1000
400

150 CHF

25 000 USD
60 000 CHF

65 000 USD

USD

, CHF

USD

1,5

$5 + 10 CHF = $10, 2:1


$ 5 * 2 = $10
, ? , ,
, ,
? :

;
( )
( ),
.

23

, ,
, .
, . ,
, DOT . ,
.
, . ,
? .
, . ( ,
, , .)
. , ?
, .
, ?
.
, ,
. , .
, ,
(API)
, ,
.
1:
public void testMultiplicationO {
Dollar five new Dollar(5);
five.times(2);
assertEqualsdO. five.amount);
}

(, : , ,
. ? , -
2, . , ,
3.)
$5 + 10 CHF = $10, 2:1
5 * 2 = $10
amount
Dollar?
?

, , ,
. ( , ,
1
2

times() * . . .
(code that smells) , (
). . .
JUnit, .
, . ,
. . .

24

, JUnit.)
( )?
:
Dollar;
;
times(int);
() amount
, . (
.) , Dollar
Dollar
class Dollar

, .
, , , -
.
Dollar
Dollar(int amount) {
}

. times().
, :
Dollar
void timesdnt multiplier) {
}

. ,
() amount.
Dollar
int amount:

! , :
. 1.1.
. (JUnit
) , ,
, 10 0.
...
! .
. , , - .

, .
. .
, ,
, .
.

25

Test class name:


MoneyJMoneyTest
! Reload classes evafy nm

Runs:
1/1

errors: Failures:
0
1

testMultipllcation(MoneY.MoneyTe80:expect*d:<10 butwas:0>

unitframeworKAsserticmFailedError: expected:10> but was <0>


at Money MoneyTesLtestMuplication(MonerTestjava:18)

1
jFinished: 0.11 seconds

. 1.1. !

, ,
:
Dollar
i n t amount= 10:

JUnK
Test class name:

. - ' - II - 1

HMoneyMoneyTest
E Retoad classes everyrun

J
Rims:

Errors: Futures:

1 / 1 0

0
f

Run

_
<i

.-.:.: :,

"..-:...?

II

1^/^ ""-"-"'^ "'

i
^ k i l e h i * 0.11 seconds

1.2.

1.2 .
, .

26

, ! , .
, ,
.
, .
, TDD :
1. .
2. , , - .
3. .
4. , .
5. .

(Steve Freeman) ,
( , , ).
,
. ,
, .
.
SQL, ,
, ,
.
.
, .

. ,
.
,
, - ,
.
TDD. , ,
, .

,
. ?
,
. ? , :
Dollar

i n t amount= 5 * 2 ;

, 10. ,
, , . 5 2
, .
10. 5 2 ,
,
. .
, 5 2 ,
. () amount
timesQ?

27

Dollar
int amount:
void timesdnt multiplier) {
amount= 5 * 2 :
}

, .
.
? , TDD
,
.
? . ,
.
. ,
, , .
,
, .
. ? ,
. 5?
,
amount:
Dollar
Dollardnt amount) {
this.amount= amount:
}

amount,
timesQ:
Dollar
void times(int multiplier) {
amount= amount * 2;
}

multiplier 2,
:
Dollar
void times(int multiplier) {
amount= amount * multiplier;
}

, Java,
*= (, , ):
Dollar
void times(int multiplier) {
amount *= multiplier:
}

28

I
$5 + 10 CHF = $10, 2:1

$ 5 * 2 = $10
amount
Dollar?
?

.
; .
:
, ;
,
;
J Unit;
, ;
, ;
, ;
,
.

:
1. . ,
. , ,
, , .
2. .
. , .
, .
, ,
.
( ),
.
, , .
3. . , ,
.
, , ,
.
, ( (Ron Jeffries) ).
,
( ). , , !
, , .
,
, ,
, .
$5 + 10 CHF = $10, 2:1

$5 * 2 - $10
amount
Dollar?
?

30

, :
Dollar . :
public void testMultiplicationO {
Dollar five= new Dollar(5):
five.times(2);
assertEqualsdO, five.amount):
five.timesO):
assertEquals(15. five.amount);
}

,
. times()
. times() ,
,
. , Dollar,
. , ,
,
.
public void testMultiplicationO {
Dollar five= new Dollar(5):
Dollar product= five.times(2);
assertEquals(10. product.amount):
product five.timesO):
assertEquals(15. product.amount):
}

,
Dollar.times():
Dollar
Dollar timesdnt multiplier) {
amount *= multiplier:
return null:
}

, . !
, Dollar
:
Dollar
Dollar times(int multiplier) {
return new DollarCamount * multiplier):
}
'
$5 + 10 CHF = $10, 2:1

$5*2 = $10
amount
Dollar?
?

31

1, ,
, .
, (
, ).
, , .
.
:
, , (Fake It)
,
;
(Obvious Implementation)
.
TDD
. , ,
( ,
, , , ).
,
, . ,
.
, , (Triangulation),
3. . :
( ) ,
(- );
, ;
, .
(, ,
) (,
Dollar) TDD. ,
.
. ,
-.

.
, ,
.

1, ,
, .
. ,
1 , ,
(, ,
, ).
,
Dollar. Value Object (-
). ,

.
Value Object
, (aliasing). ,
$5, $5
.
,
.
(aliasing).
Value Objects,
(aliasing). ($5),
($5). -
$7, .
$5 + 10 CHF = $10, 2:1

$5*2 = $10
amount
Dollar?
?
equals()

Value Objects ,
, -

33

2. , Value Objects equals(), , $5


.
$5 + 10 CHF = $10, 2:1
$ * 2 - $10
"amount"
Dollar?
?
equals()
hashCode()

, Dollar -, , equalsO, hashCode().


, .
equals()?
, . ,
, . $5
$5:
public void testEquality {

assertTrue(new Dollar(5).equals(new Dollar(5))):


}
. true :
Dollar
public boolean equals(0bject object) {
return true:
}

, , true "5==5", ,
, "amount==5", "amount==dollar.amount".
,
(Triangulation).
,
, ( ),
,
( , ).
.
, ,
, .
().
,
.
, . ,
$5 I- $6?
public void testEqualityO {
assertTrue(new Dollar(5).equals(new Dollar(5)));
2 * 297

34

assertFalse(new Doliaf(5).equals(new ())):


}
(equality):
Dollar
public boolean equals(Object object) {
Dollar dollar=(Dollar) object;
return amount == dollar.amount:
}
$5 + 10 CHF = $10, 2:1

$5 * 2 - $10
amount
Dollor?
?
equalsO
hashCodeQ


times(). $5*2 = $10 $5 * 3 - $15,
.
, . ,
, . ,
,
. ,
?
,
.
( ,
)? , , ,
.
$5 + 10 CHF = $10, 2:1

$5 * 2 - $10
amount
Dollar?
?
hashCode()
null

, (equality) .
null ?
, ,
.
, ,
Dollar. amount ,

35

.
, :
, Value Object
;
;
;
( , );
, , .

$5 + 10 CHF = $10, 2:1

$5*2 = $10
amount
D Dollar?
?
equolsQ
hashCodeO
null

, (equality),
. , Dollar.times()
Dollar,
( ), .
:
public void testMultiplicationO {
Dollar five= new Dollar(5);
Dollar product= five.times(2);
assertEquals(10. product.amount);
product^ five.timesO):
. assertEquals(15, product.amount);
}

(assert-) ,
Dollar:
public void testMultiplicationO {
Dollar five= new Dollar(5);
Dollar product=five.times(2):
assertEquals(new Dollar(lO), product);
product= five.timesO):
assertEquals(15. product.amount);
}

'

37

, :
public void testMultiplicationO {
Dollar five new Dollar(5):
Dollar product=five.times(2);
assertEquals(new Dollar(lO). product):
product five.timesO):
assertEqualsCnew Dollar(15). product):
}

product,
:
public void testMultiplicationO {
Dollar five new Dollar(5):
assertEqua1s(new Dollar(lO), five.times(2)):
assertEquals(new 0(15), five.times(3)):
}

, .
. Dollar amount, :
Dollar
private i n t amount;
$5 + 10 CHF = $10, 2:1

$5 * 2 - $10
amount
D Dollar?
?

cqualsO
hashCodeO
null

. ,
:
, ,
. TDD .
. ,
, .
, .
, , .

(-
, ).
, :

;

38

, ,
;
, ;

.

$5 + 10 CHF = $10, 2:1

$5 * 2 - $10
amount (private)
Dollar?
?
equalsO
hashCode()
null

5 CHF * 2 = 10 CHF

,
? , .
,
. , Dollar,
, .
Franc. Franc
, Dollar, ,
,
.
Franc , Dollar, ,
Dollar:
public void testFrancMultiplicationO {
Franc five= new Franc(5):
assertquals(new Franc(lO). five.times(2));
assertEquals(new Franc(15). five.timesO)):
}

(, 4
Dollar.
. , ,
, .)

40

.
? Dollar Dollar Franc.
. -. ,
.
(copy-and-paste)?
?
?
, ,
. ? ,
.
,
:
1. .
2. , .
3. , .
4. , .
5. .
, . ,
,
. TTD ,
. ,
.
TTD
. .
, .
, .
. ,
, : ,
! .
.
. .
! , ,
, (make it run, make it right).
. , ,
,
. ? , .
(
).
Franc
class Franc {
private int amount:
Francdnt amount) {

41

this.amount amount;
}
Franc timesdnt multiplier) {
return new Franc(anraunt * multiplier);
}
public boolean equals(Object object) {
Franc franc= (Franc) object;
return amount franc.amount;
}
}
$5 + 10 CHF = $10, 2:1

$5 * 2 - $10
amount (privotc)
Dollar?
?
equalsQ
hashCode()
null

5 CHF * 2 - 10 CHF
Dollar/Franc
equals()
times()

, - ,
, (Make it compile).
.
, . ,
equals(). .
,
.
:

, ;
;
,
;
,
.

$5 + 10 CHF $10, 2:1

$5 * 2 - $10
omount (private)
Dollar?
?
hashCode()
null

5 1 1 * 2 - 10
Dollar/Franc
equals()
times()

Crossing to Safety (Wallance Stegner)


.
, ,
. ,
, .
. ,
. ( -
-747. ,
.)
5 . ,
. ,
. .
,
. , ,
. ,
.
. 6.1. ( ,
, , .)

,
Dollar

Franc

43

Money

Dollar

Franc

. 6.1.

Money
equals(). :
Money
class Money

, - . ,
, ,
.
Dollar Money:
Dollar
class Dollar extends Money {
private i n t amount:
}

? . .
amount Money:
Money
class Money {
protected i n t amount:
}
Dollar

class Dollar extends Money {


}

amount :
private protected.
. (
, Money,
Dollar,
.)
equals() ,
Money.
:
Dollar
public boolean equals(Object object) {
Money dollar= (Dollar) object:
return amount == dollar.amount:
}

- .
.

44

Dollar
public boolean equals(0bject object) {
Money dollar (Money) object:
return amount == dollar.amount:
}

,
:
Dollar
public boolean equalsCObject object) {
Money money= (Money) object:
return amount money.amount:
}

Dollar Money:
Money
public boolean equalsCObject object) {
Money money (Money) object:
return amount money.amount;
}

Franc.equals().
, ,
Franc, , , Dollar,
, . ,
, .
, , TDD
, .
,
. ,
, ,
. ,
. ?
- , ,
. , ,
, - . ,
.
. ,
. . .
. .
. ,
.
, .
Dollar:
public void testEqualityO {
assertTrue(new Dollar(5).equals(new Dollar(5)));

45

assertFalse(new Dollar(5),equals(new Dollar(6))):


assertTrue(new Franc(5).equals(new Franc(5)));
assertFalse(new Franc(5).equals(new Franc(6)));

}
. !
. .
, , Franc
Money:
Franc
class Franc extends Money {
private i n t amount;
}

amount Franc,
Money:
Franc
class Franc extends Money {
}

Franc.equalsO , Money.equals().
,
Franc.
. :
Franc
public boolean equals(Object object) {
Money franc= (Franc) object:
return amount == franc.amount:
}

:
Franc
public boolean equals(Object object) {
Money franc= (Money) object;
return amount == franc.amount;
}

, , ,
, Money.
-
:
Franc

public boolean equals(Object object) {


Money money= (Money) object:
return amount = money.amount;
}

46

I
$5 + 10 CHF = $10, 2:1

$5 * 2 - $10
amount (private)
Dollar?
?

hashCodeO
null

5 CHF * 2 - 10
Dollar/Franc
equolsf)
times()
(Franc) (Dollar)

Franc.equals() Money.equals(),
Franc.
. .
?
7.
:
(Dollar)
(Money);
(Franc) (Money);
equals()
Franc.

$5 + 10 CHF = $10, 2:1


- *

-> j M

amount (privote)
Dollar?
?
cquols()
hashCode()
null

5 * 2 - 10 CHF
Dollar/Franc
cquals()
times()
(Franc) (Dollar)

6 : ,
?
. .
, ?
public void testEqualityO {
assertTrue(new Dollar(5).equals(new Dollar(5))):
assertFa1se(new Dollar(5).equals(new Dollar(6))):
assertTrue(new Franc(5).equa1s(new Franc(5)));
assertFa1se(new Franc(5).equa1s(new Franc(6))):
assertFa1se(new Franc(5).equals(new Dollar(5))):
}

.
. ,
.
, . ,
,
Money ,
amount .

48

Money
public boolean equals(Object object) {
Money money= (Money) object;
return amount =*= money.amount
&& getClassO.equals(money.getClassO):
}

,
, . ,
, ,
Java.
, ,
,
. , , .
$5 + 10 CHF = $10, 2:1

$5*2 = $10
amount (private)
D Dollar?
?
equals()
hashCode()
null

5 CHF * 2 = 10 CHF
Dollar/Franc
equals()
times()
! 1 (Franc) (Dollar)
?

times(),
.
, :
;
, ,
getClass();
-
, .

$5 + 10 CHF = $10, 2:1


$5*2 = $10
omount (privote)
! 1 Dollar?
?
equolsQ
hashCode()
null

5 CHF * 2 - 10 CHF
Dollar/Franc
equals()
times()
! 1 (Fronc) (Dollar)
?
times() :
Franc
Franc timesdnt multiplier) {
return new Franc(amount * multiplier)
} <
Dollar
Dollar times(int multiplier) {
return new DollarCamount * multiplier)
}
,
Money:
Franc
Money times(int multiplier) {
return new Franc(amount * multiplier)
}

50

Dollar
Money times(int multiplier) {
return new Dollar(amount * multiplier)
}

. ,
Money, .
.
, TDD.
? ,
, .
Money (Factory Method),
Dollar.
:
public void testMultiplicationO {
Dollar five= Money.dollar(5);
assertEquals(new Dollar(lO). five.times(2)):
assertEquals(new Dollar(15), five.times(3)):
}

Dollar :
Money
static Dollar dollar(int amount) {
return new Oollar(amount):
}

Dollar,
:
public void testMultiplication() {
Money five= Money.dollar(5):
assertEquals(new Dollar(lO). five.times(2));
assertEquals(new Dollar(15). flve.timesO)):
}

, times() Money
. ,
Money ( ?)
Money.times():
Money
abstract class Money
abstract Money times(int multiplier);

:
Money
static Money dollarCint amount) {
return new Dollar(amount):
}

51

, , , .
:
public void testMultiplicationO {
Money five= Money.do!lar(5);
' assertEquals(Money.do11ar(10), five.times(2)):
assertEquals(Money.dollar(15), five.timesO)):
}
public void testEqualityO {
assertTrueCMoney.dol1ar(5).equals(Money.dol1ar(5))):
assertFalseCMoney.dollar(5).equals(Money.dollar(6))):
assertTrue(new Franc(5).equals(new Franc(5)));
assertFalse(new Franc(5).equals(new Franc(6)));
assertFalse(new Franc(5).equals(Money.dollar(5))):
}

, .
Dollar.
,
, - .
testFrancMultiplication(),
, -
, testMultiplication().
: testFrancMultiplication()?
, ? ,
, .
public void testEqualityO {
assertTrue(Money.dollar(5).equals(Money.dollar(5)));
assertFalse(Money.dollar(5).equals(Money.dollar(6))):
assertTrue(Money.franc(5).equals(Money.franc(5)));
assertFalse(Money.franc(5).equals(Money.franc(6)));
assertFalse(Money.franc(5).equals(Money.dollar(5))):
}
public void testFrancMultiplicationO {
Money five= Money.franc(5):
assertEquals(Money.francdO). five.t1mes(2)):
assertEquals(Money.franc( 15), five.timesO)):
}

Money.franc() ,
Money.dollar():
Money
static Money francdnt amount) {
return new Franc(amount):
}

52

I
$5 + 10 CHF = $10, 2:1

$5 * 2 - $10
amount (private)
Dollar?
?
equals()
hashCodeO
null

5 * 2 - 10
Dollar/Franc
equalsQ
times()
(Franc) (Dollar)
?
testFrancMultiplicationQ?^

times().
, :

, times();
;
,
;
, ,
, .

$5 + 10 CHF = $10, 2:1

$5 * 2 -" $10
amount (private)
Dollar?
?
cquals()
hashCode()
null

5 * 2 10
Dollar/Franc
equals()
times()
(Franc) (Dollar)
?
testFrancMultiplication()?

- ,
? ,
?
?
! !
, :
? , .
, ,
Flyweight Factory () ,
.
:
public void testCurrencyO {
assertEquals("USD". Money.do!lar(l).currencyO):
assertEquals("CHF". Money.franc(1).currency());
}

54

currency () Money:
Money
abstract String currencyO:

:
Franc
String currencyO {
return "CHF":
}
Dollar
String currencyO {
return "USD";
}


,
currencyO- (
, .
, ,
. -, , ,
.)
Franc
private String currency:
FrancCint amount) {
this.amount = amount;
currency "CHF";
}
String currencyO {
return currency:
}

To Dollar:
Dollar
private String currency;
Do11ar(int amount) {
this.amount = amount:
currency = "USD";
}
String currencyO {
return currency:
}


currencyO Money, :
Money
protected String currency:
String currencyO {

55

return currency:
}

"USD" "CHF"
, ,
.
:
Franc
Franc(int amount. String currency) {
this.amount = amount:
this.currency = "CHF":
}

:
Money
static Money franc(int amount) {
return new Franc(amount, null);
}
Franc
Money times(int multiplier) {
return new Franc(amount * multiplier, null):
}

-! Franc.timesO
, ?

? ,
, .
, ,
( Jim
Coplien). , times()
:
Franc
Money timesdnt multiplier) {
return Money.franc(amount * multiplier):
}

"CHF":
Money

static Money franc(int amount) {


return new Franc(amount."CHF"):
}

, :
Franc

Franc(int amount. String currency) {


this.amount = amount:

56

I
this.currency - currency:
}

,
. ? .
,
,
.
. ,
, ,
. ,
Dollar :
Money
static Money dol1ar(int amount) {
return new Dollar(amount,"USD");
}
Dollar
Dollar(int amount, String currency) {
this.amount = amount;
this.currency = currency:
}
Money t1mes(int multiplier) {
return Money.dollar(amount * multiplier):
}

. !
TDD. ,
? .
? . TDD
, , .
- , ,
.
,
:
Money

Money(int amount. String currency) {


this.amount - amount:
this.currency - currency:
}
Franc
Francdnt amount. String currency) {
super(amount, currency);
}
Dollar
Dollar(int amount. String currency) {
super(amount. currency):
}

57

$5 + 1 0 CHF = $10, 2:1

$ * 2 - $10
omount (private)
Dollar?
?

equols()
hashCode()
null

5*2- 10
Dollar/Franc
equols()
times()
(Franc) (Dollar)
?
testFrancMultiplication()?

times() .
, :
,
, ,
;
,
( );
,
times() ;
Dollar
;

.



timesQ
$5 + 10 CHF = $10, 2:1
$ 5 * 2 $10
amount (private)
Dollar?
?
cquals()
hashCode()
null

5 CHF * 2 - 10 CI1
Dollar/Franc
equals()
times()
(Fronc) (Dollar)
?
testFrancMultiplication()?
Money,
. times() ,
:
Franc
Money timesdnt multiplier) {
return Money.franc(amount * multiplier):
}
Dollar
Money times(int multiplier) {
return Money.dollar(amount * multiplier):
}
, ,
, ,
, . , -

10 times()

59

new? ( ,
, new
. ,
. , ,
.)
Franc
Money timesCint multiplier) {
return new Franc(amount * multiplier. "CHF");
}
Dollar
Money times(int multiplier) {
return new Oollar(amount * multiplier. "USD");
}

, Franc currency
"CHF\ :
Franc
Money times(int multiplier) {

return new Franc(amount * multiplier, currency);


}
! Dollar:
Dollar
Money timesCint multiplier) {
return new Dollar(amount * multiplier.currency);

}
. ,
Franc Money? ,
,
, , ,
. ,
.
. TDD,
, 5 10
, 15
. , .
, , ,
. , ,
.
, ,
Franc.timesO Money:
Franc
Money timesCint multiplier) {
return new Money (amount * multiplier, currency):
}

60

, Money (
) :
Money
class Money
Money times(int amount) {
return null:
}

.
: expected:<Money.Franc@31aebf> but was:<Money.Money@478a43>. He -
. , .
, toString():
Money
public String toStringO {
return amount + " " + currency:

, ! ?! ? ,
toStringO, , :
;
toStringO , ,
, ;
,
, .
.
: expected :< 10
CHF> but was: < 10 CHF>. , .
,
. equals():
Money
public boolean equals(Object object) {
Money money = (Money) object;
return amount == money.amount
&& getClassO.equals(money.getClassO);
}

,
.
, .
,
, .
, ,
. .
equalsQ,
.

10 times()

61

. (
, ,
, .)
Franc

Money timesdnt multiplier) {


return new Franc (amount * multiplier, currency):
}

. ,
Franc(10,"CHF") Money(10,"CHF"), ,
. :
public void testOifferentClassEqualityO {
assertTrue(new MoneydO, "CHF").equals(new FrancdO. "CHF"))):
}

, . equal()
, :
Money
public boolean equals(Object object) {
Money money - (Money) object:
return amount == money.amount
&& currencyO.equals(money.currencyO);
}

Franc.times() Money,
- :
Franc
Money times(int multiplier) {
return new Money(amount * multiplier, currency);
}

DoHar.times()?
Dollar
Money timesdnt multiplier) {
return new Money (amount * multiplier, currency):
}

! ,
.
Money

Money timesCint multiplier) {


return new Money(amount * multiplier, currency);
}

62

I
$5 + CHF = $10, 2:1/

$5 * 2 - $10
amount (private)
Dollar?
?

equals()
hashCode()
null

5 CHF * 2 - 10 CHF
Dollar/Franc
equalsQ
times()
() (Dollar)
?
testFrancMultiplicationO?

, ,
.
:
times(),
, (Inline Method),
;
toString() ;
(
Money Franc) , ,
;
,
.

$5 + 10 CHF = $10, 2:1

$5 * 2 - $10
amount (private)
Dollar?
?
equalsO
hashCode()
null

5 * 2 - 10
Dollar/Franc
equals()
timcs()
(Franc) (Dollar)
?
testFrancMultiplication()?

, Dollar Franc, ,
,
. .
,
. Franc:
Franc
static Money francdnt amount) {
return new Money (amount. "CHF"):
}

Dollar:
Dollar

static Money dollar(int amount) {


return new Money (amount. "USD"):
}

64

Dollar , .
Franc:
public void testDlfferentClassEquality() {
assertTrue(new MoneydO, "CHF").equals(new FrancdO. "CHF")));
}

,
, .
:
public void testEqualityO {
assertTrue(Money.dollar(5).equals(Money.dollar(5))):
assertFalse(Money.dollar(5).equals(Money.doll ar(6))),
assertTrue(Money.franc(5).equals(Money.franc(5)));
assertFalse(Money.franc(5).equals(Money.franc(6)));
assertFalse(Money.franc(5).equals(Money.dollar(5))):
}

,
. , .
assert,
:
public void testEqualityO {
assertTrue(Money.dollar(5).equals(Money.dollar(5)));
assertFalse(Money.dollar(5).equals(Money.doll a r ( 6 ) ) ) :
assertFalse(Money.franc(5).equals(Money.dollar(5))):
}
$5 + 10 CHF = $10, 2:1
$ 5 * 2 = $10
amount (private) 1
Dollar?
?
eqaalsO
hashCode()
null

..5 * 2-1 10
Dollar/Franc
equals()
times()
() (Dollar)
?
testFrancMultiplication()?

testDifferentClassEqualityO , ,
, , .
,
. Dollar -

11

65

Franc. ,
: Money. ,
testDifferentClassEqualityO . ,
Franc.
,
.
, , ,
, ( ,
). ,
testFrancMultiplication(), , -
.
,
.
. :
;
,
,
.

3 .297

,
-

$5 + 10 CHF = $10, 2:1

, ,
.
. (
. , ,
,
. - ,
, . .)
$5 + 10 CHF = $10, 2:1

$5 + $5 = $10
,
,
: $5 + $5 = $10.
public void testSimpleAddition<) {
Money sum Money.dollar(5).plus(Money.donar(5)):
assertEquals(Money.dollar(10). sum);
}

, Money.dollar( 10),
. :
Money
Money pi us(Money addend) {
return new Money(amount + addend.amount, currency):
}

( ,
. , ,
. , ,
TDD .)

12 , -

67

, ,
. ,
.
.
.
? ,
.
,
, , ,
- . ,
, .
,
( ,
).
(
) .
,

,
.
. ,
, ,
, .
Imposter ().
, .
Imposter ()?
,
.
. ,
. , TDD
. , TDD
, ,
,
.
, ?
, Money,
Money. ,
. ,
.
.
: (expression).
, : (2 + 3) * 5.
, : ($2 + 3 CHF) * 5.
Money .
Expression.

68

Sum1. (,
) ,
Expression .

.
? , ,
:
public void testSimpleAdditionO {
assertEquals(Money.dollardO). reduced):
}

reduced Expression,
Expression, -
.
? . , ,
:
public void testSimpleAdditionO {
Money reduced= bank, reduce (sum. "USD"):
assertEquals(Money.dollar(10), reduced):
}

(, :
.
. , , ,
- .)
: reduce()
bank. :
...reduced= sum.reduceC'USD", bank).

reduce()
bank? : ,
, .
reduce()
Bank, Expression?
:
Expression ,
. , ,
.
(
,
).

sum . . .

12 , -

69

, Expression
. ,

, . Expression
.
, ,
- ,
, . ,
, Bank,
reduce() Expression.
bank, , :
public void testSimpleAddition() {
Bank bank= new BankO;
Money reduced bank.reduce(sum. "USD");
assertEquals(Money.dollar(10). reduced);
}

Money Expression:
public void testSi mpl eAddi t i o n O {
Expression sum= f i v e . p l u s ( f i v e ) :
Bank bank= new BankO;
Money reduced= bank.reduceCsum, "USD"):
assertEquals(Money.dollar(10). reduced);
}

, , ,
:
public void testSi mpleAddi t i on() {
Money five= Money.dollar(5):
Expression sum= f i v e . p l u s ( f i v e ) :
Bank bank= new BankO:
Money reduced= bank.reduce(sum. "USD");
assertEquals(Money.dollar(10). reduced);
}

, ?
Expression ( ,
):
Expression
interface Expression

Money. plus() Expression:


Money

Expression plus(Money addend) {


return new Money(amount + addend.amount. currency):
}

70

, Money Expression (
, ):
Money
class Money implements Expression

, Bank:
Bank
class Bank

reduceQ:
Bank

Money reduce(Expression source. String to) {


return nul1:
}

. !
! :
Bank

Money reduce(Expression source. String to) {


return Money.dollar(lO):
}

! .
. :
,
( $5 + 10 CHF
$5 + $5);

;
;
;
, ;
, ,
, .

$5 + 10 CHF = $10, 2:1


$ 5 + $5 = $10

$5 + $5 ,
. .
, $10
:
Bank
Money reduceCExpression source. String to) {

return Money.dollar(10);
*
}
$5 + $5 :
public void testSimpleAdditionO {
Money five= Money.do11ar(5):
Expression sum= five.pi us(five);
Bank bank= new BankO:
Money reduced= bank.reduce(sum, "USD");
assertEquals(Money.dollar(10). reduced);
}

, , ,
.
.
, . ,
, .
$5 + 10 CHF = $10, 2:1

$5 + $5 = $10
$5 + $5 Money

72

, Money. plus() Money,


(Expression), (Sum). (,
,
.)
, Money
Sum:
public void testPlusReturnsSumO {
Money five= Money.do11ar(5):

Expression result= five.pi us(five);


Sum sum= (Sum) result:
assertEquals(five. sum.augend):
assertEquals(five. sum.addend):
}

( - ,
augend, addend}
, .)
.
,
.
, ,
1. , Sum
: augend addend:
Sum
class Sum {

Money augend;'
Money addend:
}

(ClassCastException) Money.plus() Money, Sum:


Money
Expression pi us(Money addend) {
return new Sum(this, addend);
}

Sum :
Sum
Sum(Money augend, Money addend) {

, Sum Expression:
Sum
class Sum implements Expression

, - ,
Sum ( -

13

73

, ,
):
Sum

Sum(Money augend. Money addend) {


this.augend- augend:
this.addend=' addend:
}

Bank.reduce() Sum.

, , Money,
.
public void testReduceSumO {
Expression sum= new Sum(Money.do11ar(3). Money.dollar(4));
Bank. bank= new BankO:
Money result= bank.reduce(sum, "USD"):
assertEquals(Money.dollar(7). result);
}

,
. ( reduce())
Sum , (
) Money, (amount)
Money, Sum,
(currency) :
Bank
Money reduce(Expression source. String to) {
Sum sum= (Sum) source:
i n t amount= sum.augend.amount + sum.addend.amount:
return new Money(amount, t o ) ;
}

:
( Sum),
Expression;
.
. Sum
.
Bank

Money reduce(Expression source. String to) {


Sum sum= (Sum) source:
return sum.reduce(to);
}
Sum
,
public Money reduce(String to) {
int amount- augend.amount + addend.amount;

74

I
return new Money(amount, t o ) :
}

. (reduce)
, Sum .
Bank, , ,
Sum.reduceQ Bank.
.
, . ( ,
, - ,
, , .)

$5 + 10 CHF = $10, 2:1


$5 + $5 = $10
$5 + $5 Money
Bank.reduce(Money)

, * Bank.reduce()
Money?
, ,
- :
public void testReduceMoney() {
Bank bank= new BankO;
Money resu1t= bank. reduce(Money.dol l a r d ) . "USD");
assertEquals(Money.dol1ar(l), result):
}
Bank
Money reduce(Expression source. String to) {
i f (source instanceof Money) return (Money) source:
Sum sum= (Sum) source;
return sum.reduce(to);
}

! !
. ,
, . Sum
reduce(String), Money
, reduce(String) Expression.
Bank
Money reduce(Expression source. String to) {
i f (source instanceof Money)
return (Money) source.reduce(to):
Sum sum= (Sum) source;
return sum.reduce(to):
}
Money
public Money reduce(String to) {
return t h i s ;
}

13

75

reduce(String) Expression:
Expression
Money reduce(String t o ) ;


:
Bank
Money reduce(Expression source. String to) {
return source.reduce(to):
}

" , Expression Bank


, .
Java.
, ,
Bank.reduce(Expression, String) Expression.reduce(String)
. ,
,
.
$5 + 10 CHF = $10, 2:1

$5 + $5 = $10
$5 + $5 Money
Bank.reduce(Money)
Money
Reduce(Bank,String)


.
:
,
;
, ,
;
, , ,
, ( Sum);
( Sum);
,
, , ;
, . ().

$5 + 10 CHF = $10, 2:1


$5 + $5 = $10
$5 + $5 Money
Bonk.rcducc(Moncy)
Money
Reduce(Bank,String)

, , (
(embrace
change))1. ,
. :
public void testReduceMoneyDifferentCurrencyO {
Bank bank= new BankO;
bank.addRateC"CHF", "USD". 2);
Money result= bank.reduce(Money.franc(2). "USD");
assertEquals(Money.dollard), result);
}

, (
- ,
). ,
:
Money

public Money reduce(String to) {


int rate - (currency.equalsC'CHF") && to.equals("USD"))
? 2
: 1:
return new Money(amount / rate, to):
}
( change , ),
- Extreme Programming Explained: Embrace Change.
: . . .: , 2002. 224 . . .

14

77

, Money . .
, ,
, Bank. Bank
Expression.reduce(). ( ? , .
.) :
Bank
Money reduce(Expression source. String to) {
return source.reduce(this. to):
}

:
Expression
Money reduceCBank bank. String t o ) ;
Sum

public Money reduceCBank bank, String to) {


int amount= augend.amount + addend.amount:
return new Money(amount, to):
}
Money
public Money reduceCBank bank. String to) {
int rate - (currency.equalsCCHF") && to.equalsCUSD"))
? 2
: 1:
return new MoneyCamount / rate, to);
}

(public),
( , , ).
Bank:
Bank
i n t rateCString from. String to) {
return (from.equalsCCHF") && to.equalst"USD"))
? 2
: 1:
}

bank :
Money
public Money reduceCBank bank. String to) {
i n t rate = bank.rate(currency. t o ) ;
return new Money(amount / rate, t o ) ;
} .

2 ,
. ,
Bank -

78

.
-,
.
, ? Array.equals()
?
public void testArrayEqualsO {
assertEquals(new Object[] {"abc"}. new Object[] {"abc"}):
}

. . ,
-:
Pair
private class Pair {
private String from:
private String to;
Pair(String from, String to) {
this.from- from:
this.to- to:
}
}

Pair ,
equals() hashCode().
, . ,

. , .
, -,
, ,
,
.
Pair
public boolean equals(0bject object) {
Pair pair= (Pair) object:
return from.equals(pair.from) && to.equals(pair.to);
}
public int hashCodeO {
return 0;

0 -,
, .
. ,
, ,
.
,
:

14

79

Bank
private Hashtable rates- new HashtableO;

:
Bank
void addRate(String from. String to. int rate) {
rates.put(new Pair(from. to), new Integer(rate)):
}

:
Bank
int rate(String from. String to) {
Integer rate= (Integer) rates.get(new Pair(from, to));
return rate.intValueO:
}

- ! . ?
, ,
. , USD USD
1, , .
, :
public void testldentityRateO {
assertEqualsd. new BankO.rateCUSD". "USD")):
}

,
:
Bank
int rate(String from. String to) {
i f (from.equals(to)) return 1;
Integer rate= (Integer) rates.get(new Pair(from, to));
return rate.intValueO:
}

!
$5 + 10 CHF = $10, 2:1

$5 I 5 - $10
$5 + $5 Money
Bank.rcduco(Money)
Money
Reduce(Bank,String)

, , $5 + 10
CHF. :
, , , ;
;

80

(testArrayEquals),
Java;
(private) ,
;
, ,
.

$5 + 10 CHF = $10, 2:1


$5 I $5 = $10
$5 + $5 Money
Bonk.reduce(Money)

Rcduce(Bonk,String)

, , $5 + 10 CHF:
public void testMixedAdditionO {
Expression fiveBucks= Money.dollar(5):
Expression tenFrancs= Money.franc(lO);
Bank bank= new BankO:
bank.addRate("CHF". "USD". 2):
Money result= bank.reduce(fiveBucks.plus(tenFrancs). "USD"):
assertEquals(Money.dollar(10). result):
}

. ,
. ,
Money Expression , ,
, , .
.

. ,
.
. ,
. :
, .
(
).
public void testMixedAdditionO {
Money fiveBucks= Money.dollar(5);

82

I
Money tenFrancs= Money.franc(10);
Bank bank= new BankO:
bank.addRateC'CHF", "USD". 2):
Money result= bank.reduce(fiveBucks.plus(tenFrancs). "USD"):
assertEquals(Money.dollar(10), r e s u l t ) :
}

. 15 USD 10 USD. ,
Sum.reduce() :
Sum
public Money reduce(Bank bank. String to) {
i n t amount= augend.amount + addend.amount;
return new Money(amount, t o ) :
}

, :
Sum
public Money reduceCBank bank, String to) {
i n t amount= augend.reduce(bank, to).amount
+ addend.reduce(bank, to).amount:
return new Money(amount, t o ) :
}

, . Money
Expression. ,
. , augend addend
Expression:
Sum
Expression augend:
Expression addend:

Expression:
Sum
Sum(Expression augend. Expression addend) {
this.augend= augend:
this.addend= addend:
}

( Sum Composite, ,
.) Sum, , ,
Money?
plus() Expression:
Money

Expression pi us(Expression addend) {


return new Sum(this, addend):
}

15

83

times() Expression:
Money

Expression times(int multiplier) {


return new Money(amount * multiplier, currency);
}

, Expression
plus() times(). Money .
plusQ :
public void testMixedAdditionO {
Money fiveBucks= Money.dollar(5);
Expression tenFrancs= Money.franc(10);
Bank bank= new BankO;
bank.addRateC'CHF". "USD". 2);
Money result= bank.reduce{fiveBucks.pl us(tenFrancs). "USD");
assertEquals(Money.dollardO), r e s u l t ) ;
}

tenFrancs Expression, ,
. ,
, . :
public void testMixedAdditionO {
Expression fiveBucks= Money.dollar(5):
Expression tenFrancs= Money.franc(10):
Bank bank= new BankO;
bank.addRateC'CHF". "USD". 2);
Money result= bank.reduce(fiveBucks.plus(tenFrancs). "USD");
assertEquals(Money.dollardO), r e s u l t ) ;
}

, plus()
Expression. :
Expression
Expression pi us(Expression addend);

Money Sum. Money? ,


(public) Money:
Money
public Expression plusCExpression addend) {
- return new SunKthis. addend):
}

Sum,
:
Sum

public Expression plusCExpression addend) {


return nul1;
}

84

I
$5 t 10 IF - $10, 2:1

$5 I $5 - 10
$5 + $5 Money
Dank.rcduce(Moncy)

Reduce(Bank,String)
Sum.plus
Expression.times

.
Money Expression.
, . :
, ,
;
( );
,
(Expression fiveBucks),
( plus()
Expression . .).

,
-!

$5 10 CHF = $10, 2;1


$5 I $5 - $10
$5 + $5 Money
Bank.reducc(Money)
Money
Reduce(Bank,String)
Sum.plus
Expression .times

Expression.plus,
Sum.plus(). Expression.times()
. Sum.plus():
public void testSumPlusMoneyO (
Expression fiveBucks= Money.dollar(5):
Expression tenFrancs= Money.franc(lO):
Bank bank= new BankO;
bank.addRate("CHF". "USD". 2);
Expression sum= new SurrKfiveBucks. tenFrancs).plus(fiveBucks);
Money result= bank.reduce(sum. "USD"):
assertEquals(Money.dollar(15). result):
}

Sum fiveBucks tenFrancs,


, Sum, .
,
, ,
. ,
. , , ,
.
, .
, Money (,
):

86

Sum
public Expression pi us(Expression addend) {
return new SunKthis. addend):
}
$5 i 10 = $10, 2:1

$5 i $5 - $10
$5 + $5 Money
Bank.rcducc(Moncy)
Money
Reducc(Bank,String)
Sum.plus
Expression.times

TDD ,
,
. TDD ,
, ,

, , .
. , ,
, ,
.
$5 i 10 CHF - $10, 2:1

$5 I $5 - $10
$5 + $5 Money
Bank.reducc(Money)
Money |
Rcducc(Sank,String)
Sum.plus
Expression.times

Sum.times(), ,
Expression.times() . :
public void testSumTimesO {
Expression fiveBucks= Money.dollar(5);
Expression tenFrancs= Money.franc(10);
Bank bank= new BankO;
bank.addRate("CHF". "USD", 2);
Expression sum= new $um(fiveBucks,tenFrancs).times(2):
Money result= bank.reduce(sum. "USD");
assertEquals(Money.dollar(20), result):
}

, , (,
J Unit, , .
Fixture () ,
xUn.it.)

16 , -!

87

Sum
Expression times(int multiplier) {
return new Sum(augend.times(multiplier).addend.times(multiplier))
}

augend addend
Expression, , ,
Expression times():
Expression
Expression times(int multiplier);

Money.times()
Sum.times() ( ):
Sum
public'Expression timesdnt multiplier) {
return new Sum(augend.times(multiplier),addend.times(multiplier));
}
Money
public Expression times(int multiplier) {
return new Money(amount * multiplier, currency):
}
$5 i 10 - $10, 2:1
5 I $5 - $10
$5 + $5 Money
Bonk.reduce(Moncy)
Money
Rcducc(Bank,String)
Sum.plus
Expression.times

.
,
$5 + $5 Money. :
public void testPlusSameCurrencyReturnsMoneyO {
Expression sum= Money.dollard).plus(Money.dollar(D);
assertTrue(sum instanceof Money):
}

,
, .
, , , ,
. , ,
:
Money
public Expression plus(Expression addend) {
return new Sum(this, addend);
}

88

I
$5 i 10 CHF - $10, 2;1
$5 I $5 = $
$5 i $5 Money
Bank.rcduce(Moncy)

Reducc(Bonk,String)
Sum.plus
Exprcssion.timcs

He ,
Money ( ,
, ).
, ( ).
. :
,
, ;
, TDD
,
;


, ;
, ,
, .


.
. ,
:
? ?
. ,
.
JUnit.
JUnit?
. .
. : .
?
. TDD
?

?
? .
Sum.plus() Money.plus().
Expression ( ,
),
.
,
. TDD ,
.
, , ,
. ,
, .
,

90

. ,
, ,
, .
,
(, SmallLint for Smalltalk).
.
.
, , .
:
? ,
, , , , .
, . ,
, ,
,
, .
, , .
?
, ?
( .)

,
,
,
.
,
( ). ,
.

( , ,
). ,
,
(
). ,
,
(expressions).
, .
, .
, . ?
, .
,
,
. , ,
.

17

91

(MoneySum),
(MoneyBag) , ,
(Wallet). ,
. ,
( Money) . , 2 USD +
+ 5 CHF + 3 USD 5 USD + 5 CHF.
.

, .
, - . ,
, , ,
,
.
,
20- ? ?
, ,
, ?
?

JUnit
JUnit
. ,
Enter 125 .
, ,
. ,
.
. 17.1 .
, , ,
.
50
45

40
35

>. 30

25

20

s. 15

10
5
0

1
<5
< 10

>= 10

. 1 7 . 1 .

92


. 17.1 .
17.1.

W

(2)

5
22
91
1,04
4,1 W

1
15
89
1
5,9

:
1. (API) ,

.
.
,
.
2. ,
(fixtures).

.
3. (cyclomatic complexity) ,
.
1,
, .
,
.
4.
.
5.
,
. 29,
xUnit.

TDD :
;
, ;

17

93

;
, ;
, .
, ,
, ,
? (
.) . 17.2
,
.
10

><
6
05

I 2

, , ,.,, ,, ,

I I I I I I I I I''
10 12 14 16 18 20 22 24 26 28

. 17.2. ,

,
, , ,
, ( ,
, , , ,
).
, ,
( )
, ,
.
,
1.


TDD. TDD
, , -

Mandelbrot, Benoit, ed. 1997. Fractals and Scaling in Finance. New York: Springer-Verlag.

94

.
:
;
;
.
,
TDD, ,
.
, TDD
,
, , , ,
.
?
:
(statement coverage).
,
.
TDD, 100 %.

. , J Probe (www.sitaka.com/software/
jprobe) ,
Money.toString(). ,
,
.
(defect insertion).
. :
, .
, ,
Jester Oester.sourceforge.net). ,
, ,
. : Pair.hash.Code().
-
: 0. ,
( ),
, .
(Phlip), ,
.
: ,
,
, (
). .
-, . ,
, TDD,

17

95

. ( 32 ,
6 , , , 65
.)
.

.
:
( ,
),
.


, ,
TDD:
, ,
: (Fake It), (Triangualation) (Obvious Implementation);

;
:
, ;
, .

II
xUnit


, ? ,
, .
xUnit Python,
Python. He
, , Python,
.
, , -,
Python, -, ,
, , -
,
TDD !


xUnit


-,
. (
! ! ! .)
.
,
, . II
,
. , ,
.
,
. : TestCase("testMethod").run(). :
,
.
,
. , , ,
, , . ,
, - ,
, . ,
,
:

setup
tearDown
tearDown ,

, - .
- ,
,

18 xUnit

99

, . ,
, ,

, .
,
.
, . ,
. .
.
.
.
WasRun1, ,
. wasRun
( , wasRun ).
( WasRun) test.
, assert test.wasRun ( assert
Python).
Python
, .
, ,
:
t e s t - WasRunC'testMethod")
print test.wasRun
test.testMethodO
print test.wasRun

, None
1 . ( Python None
null nil 0 .)
, . ,
WasRun ( !).
WasRun

class WasRun:
pass

( pass ,
.) , WasRun
wasRun.
( ), (
init ).
wasRun None ():
WasRun

class WasRun:

was . . rupee.

100

II xLlnit
def

i n i t (self, name):
self.wasRun= None

None,
,
WasRun testMethod. ( ,
: - - , . ,
? , , IDE
.)
WasRun
def testMethod(self):
pass

: None None1.
None 1. , testMethod
wasRun :
WasRun
def testMethod(self):
self.wasRun= 1

, , ! .
--! ,
, , .
, , ,
run().
:
test= WasRun("testMethod")
print test.wasRun
test.runO
print test.wasRun

,
:
WasRun
def run(self):
self.testMethod(-)

, .
,
, , ,
. ,
. , , .
Python () def,
. , .
( ). . .

18 xUnit

101

, TestCase,
.
testMethod.
Python
(.
WasRun). , ,
, .
1.
WasRun

class WasRun:
def __init (self, name):
self.wasRun= None
self.name* name
def run(self):
method = getattr(self. self.name)
method 0

: ,
, ,
,
. ,
( ).
. TDD :
TDD ,
. ,
.
WasRun :
-, , ; -,
. (
). ,
TestCase WasRun :
TestCase
class TestCase:
pass
WasRun
class WasRun(TestCase): . . .

name :
TestCage
def __init__(self, name):
self.name= name

(Duncan Booth) , ,
Python, ,
.

102

II xUnit

WasRun
def i n i t (self, name):
self.wasRun= None
TestCase. i n i t (self, name)

, ; run() ,
, , . (
.)
TestCase
def _ i m ' t _ ( s e l f . name):
self.name= name
def run(self):
method - getattKself. self.name)
method

,
, , .
,
: None 1. ,
:
TestCaseTest
class TestCaseTest(TestCase):
def testRunning(self):
test WasRunCtestMethod")
assert(not test.wasRun)
test.runO
assert(test.wasRun)
TestCaseTest("testRunning").run()

setup
tearDown
tearDown ,


print,
assert, , ,
Extract Method ( ).
. ,
, . ,
,
(,
Python).
, ,
. , ,
, TDD.

18 xLlnit

103

,
. TDD,
,
. , TDD,
, .
setUp().
.
:
, - ;

,
;
Pluggable Selector ( )

, ;
.


( setup)

, ,
( (Bill Wake)
Arrange, Act, Assert):
Arrange;
Act;
Assert.

. setup
~v tearDown
- tearDown ,


Arrange ,
.
: 7 9. , 16;
, -2; , , ,
63. ,
: 7 9.
(
), ,
.
:
. ,
. ,
, ,
.
. ,
.

19 ( setUp)

105


- , , ,
.

: ,
, ,
. ,
:
, , ,
. , , , , ,
- , , .
, . ,
.

.
WasRun, , wasRun
. :
TestCaseTest
def testSetUp(self):
test= WasRunC'testMethod")
test.runO
assert(test.wasSetUp)

,
TestCaseTest("testSetUp").run(). ,
wasSetUp . ,
. :
WasRun
def setUp(self):
self.wasSetUp= 1

setUpQ - .
setllpO TestCase. :
TestCase
def setUp(self):
pass
def run(self):
self.setUpO
method = getattr(self, self.name)
method

,
, , .
? , . - ,
, ,
.

106

xUnit

,
. WasRun,
wasRun setUp():
WasRun
def setUp(self):
self.wasRun= None
self.wasSetUp= 1

testRunning()
,
. ?
, testSetUp().

, , :
TestCaseTest
def testRunning(self):
test WasRunC'testMethod")
test.runO
assert(test.wasRun)

.
WasRun, .
WasRun setUp(), .
TestCaseTest,
. ( ,

,
.)
TestCaseTest
def setUp(self):
s e l f . t e s t - WasRunC'testMethod")
def testRunning(self):
self.test.runO
assert(self.test.wasRun)
def testSetUp(self):
self.test.runO
assert(self.test.wasSetUp)

setup
tearDown
tearDown ,


,
tearDownQ.

19 ( setup)

107

:
, ,
;
setUp() ;
setUp() ,
- ;
setUp() , ,
( ,
).


(
tearDown)

TGCTODOrO

setup
tearDown
tearDown ,

.
,
setUp(). , ,
.
tearDown(),
.
tearDown()?
.
. ,
: setUp()
, tearDown()
. ,
. ,
.
.
, , .

setup
tearDown
tearDown ,



WasRun

20 ( tearDown)

109

WasRun
def setUp(self):
self.wasRun= None
self.wasSetUp= 1
self.log= "setUp "

testSetUpO,
:
TestCaseTest

def testSetl)p(self):
self. test. runO
assert("setUp " self.test.log)

wasSetUp.
:
WasRun
def testMethod(self):
self.wasRun= 1
s e l f . l o g - self.log + "testMethod "

testSetUpQ,
"setUp testMethod ".
:
TestCaseTest

def testSetUp(self):
self.test.run()
assertC'setUp testMethod " self.test.log)

,
testRunning testSetUp:
TestCaseTest
def setUp(self):
s e l f . t e s t = WasRun("testMethod")
def testTemplateMethod(self):
self.test.run()
assert("setUp testMethod " == test.log)

WasRun ,
, setUp():
TestCaseTest
def testTemplateMethod(self):
test= WasRun("testMethod")
test.run()
assertC'setUp testMethod " == test.log)

, ,
,
. , -

110

II xLlnit

,
, , ,
. ,
.
,
, .
.
, , ,
, , .

setup
tearDown
tearDown ,


D WasRun

tearDown(). ! !
tearDown():
TestCaseTest
def testTemplateMethod(self):
test- WasRunCtestMethod")
test.runO
assert("setup testMethod tearDown " test.log)

. ,
:
TestCase
def run (self, result):
result. testStartedO
self.setUpO
method = getattrCself. self.name)
method()
self, tea rDownO
WasRun
def setUp(self):
self.1og= "setup "
def testMethod(self):
self.log= self.log + "testMethod "
def tearDown(self):
self.log= self.log + "tearDown "

WasRun, TestCaseTest
tearDownQ TestCase:
TestCase
def tearDown(self):
pass

20 ( tearDown)

111

, .
! . ,
, , .

setup
tearDown
tearDown ,


Wasftun

.
Python

.
:
;
tearDown()
;
, , ,
.


sctUp
tcorDown
tearDown ,


WoaRun

tearDown() ,
. , ,
. ( ,
, , ).
,
,
.
TDD , ,
. , ,
, , -,
, -, , ,
. ,
,
. ,
. , ,
,
.
,
, : 5, 2: TestCaseTest.testFoo ZeroDivideException, MoneyTest.testNegation AssertionError.
,
.
.

21

113

TestCase.run() TestResult,
( ,
).
TestCaseTest
def testResult(self):
test= WasRunC'testMethod")
result= test.runO
assertC'l run. 0 failed" == result.summaryO)

:
TestResult
class TestResult:
def summary(self):
return "1 run, 0 failed"

, TestResult
TestCase.run():
TestCase
def run(self):
self.setUpO
method = getattr(self. self.name)
method
self .tea rDownO
return TestResultO

, ,
summaryO . , .
:
TestResult
def i n i t (self):
self.runCount= 1
def summary(self):
return "% run. 0 failed" % self.runCount

( % Python sprintf .)
runCount , ,
, .
0,
.
TestResult
def i n i t (self):
self.runCount= 0
def testStarted(self):
self.runCount- self.runCount + 1
def summary(self):
return "% run, 0 failed" % self.runCount

114

II xLlnit

TestCase
def run(self):
result= TestResultO
result. testStartedO
self.setUpO
method = getattr(self, self.name)

method
self .tea rDownO
return result

"",
, ,
runCount, .
:
TestCaseTest
def testFailedResult(self):
test= WasRurK "testBrokenMethod")
result= test.runO
assertCl run. 1 failed", result.summary)

:
WasRun
def testBrokenMetnod(self):
raise Exception

setup
._. tearDown .
,. - tearDown ,



WosRun
,

, ,
WasRun.testBrokenMethod(), .
,
. .
. :

;
;
,
, .


:> ' 1"
setup '
. tearDown
tearDown ,

.

D WasRun

, ,

:
TestCaseTest
def testFailedResultFormatting(self):
result= TestResultO
result.testStartedO
'result.testFailedO
assertC'l run, 1 failed" result.summaryO)

: testStartedO testFailed().
, ,
. testFailedResultFormatting() ,
,
.
, ,
testStartedO testFailed()
, .
testStartedO testFailed(),
: :

116

II xUnit

TestResult
def i n i t (self):
self.runCount= 0
self.errorCount= 0
def testFailed(self):
self.errorCount= self.errorCount + 1

(
, , , ), ,
:
TestResult
def summary(self):
return "% run, % failed" % (self.runCount. self.failureCount)

, testFailed()
, .
? ,
:
TestCase
def run(self):
result= TestResultO
result. testStartedO
self.setUpO
try:
method = getattr(self, self.name)
method0
except:
result.testFailedO
self, tea rDownO
return result

:
setUpQ, . ,
.
, . (
TDD, 12 .
, .
,
,
. , ,
, ,
, .)
(
).

22

117


setup
tearDown
tearDown ,



| D WasRun

setup

,
.
:
;
;
, ,
;
, ,
,
.


"-- -setUp
-tearDown
tearDown ,



| WosRun

setup

xUnit,.He TestSuite,
. ,
, :
pri nt
print
print
print

TestCaseTest("testTemplateMethod").run().summary()
TestCaseTest("testResult").run().summary()
TestCaseTest("testFailedResultFormatting").run().summary0
TestCaseTest("testFailedResult").run().summary()

, ,
.
. ( ,
, ,
.) ,
TestSuite, ,
Composite (), ,
, .
( TestSuite),

:

23

119

TestCaseTest
def testSuite(self):
suite= TestSuiteO
suite.add(WasRun("testMethod"))
sui te.add(WasRun("testBrokenMethod"))
resu1t= suite.run()
assert("2 run, 1 f a i l e d " = result. summaryO)

, TestSuite
add(), ,
, :
TestSuite

class TestSuite:
def i n i t (self):
self.tests [ ]
def add(self, test):
self.tests.append(test)

( Python [] .)
run() . ,

TestResult , :
TestSuite

def run(self):
result= TestResultO
for test in tests:
test.run(result)
return result

for test in tests


tests, .
Composite () ,
, .
TestCase.runQ, ,
TestSuite.rurt().
:
Python
. ,
, ,
TestResult.
TestResult,
, TestResult.
, ,
.
TestResult .
TestResult .
Collecting Parameter ( ).

120

II xUnit

TestCaseTest
def testSuite(self):
suite= TestSuiteO
suite.add(WasRun("testMethod"))
suite.add(WasRun("testBrokenMethod"))
result= TestResultO
suite.run(result)
assert("2 run. 1 failed" = result.summaryO)

run()
:
TestSuite
def rurKself, result):
for test in tests:
test.run(result)
TestCase
def run(self, result):
result. testStartedO
self.setUpO
try:
method = getattr(self. self.name)
methodO
except:
result.testFailedO
self, tea rDownO


:
suite= TestSuiteO
sui te.add(TestCaseTest("testTemplateMethod"))
suite. addCTestCaseTestC'testResult"))
suite.add(TestCaseTest("testFailedResultFormatting"))
suite.add(TestCaseTest("testFailedResult"))
suite.add(TestCaseTest("testSuite"))
result= TestResultO
suite.run(result)
print result.summary()

BLOOD setup
tearDown
tearDown ,



WasRun

setup
TestSuite TestCase

23

121

, ,
,
TestCase.

( run()
):
TestCaseTest
def testTemplateMethod(self):
test= WasRunC'testMethod")
result TestResultO
test.run(result)
assert("setup testMethod tearDown " test.log)
def testResult(self):
test= WasRunC'testMethod")
result= TestResultO
test.run(result)
assertC'l run, 0 failed" result.summaryO)
def testFailedResult(self):
test= WasRunC'testBrokeriMethod")
result= TestResultO
test.run(result)
assertC'l run. 1 failed" == result.summaryO)
def testFailedResultFormatting(self):
result= TestResultO
result.testStartedO
result.testFailedO
assertC'l run. 1 failed" == result.summaryO)
, TestResult
set(Jp().
,
:
TestCaseTest
def setUp(self):
self.result= TestResultO
def testTemplateMethod(self):
test= WasRunC'testMethod")
test.run(self.result)
assert("setup testMethod tearDown " == test.log)
def testResult(self):
test WasRunC'testMethod")
test.run(self.result)
assertC'l run, 0 failed" self.result.summaryO)
def testFailedResult(self):
test= WasRunC'testBrokeriMethod")
test.run(self.result)
assertC'l run, 1 failed" self .result. summaryO)
def testFailedResultFormatting(self):
self, result. testStartedO

122

I I xllnit

self.result.testFai ledC)
assertC'l run. 1 failed" = self.result.summaryO)
def testSuite(self):
suite= TestSuiteO
suite.add(WasRun("testMethod"))
suite. add(WasRun("testBrokenMethod'"))
suite.run(self.result)
assert("2 run. 1 f a i l e d " self.result.summaryO)

sotUp
tcorDown
tearDown ,

! iHc i

| WasRun
-
setUp
TestSuite TestCase

self ,
Python . -
, , ,
. Python

( , ).
,
self .
.
, TDD .
, , :
TestSuite;
,
( ,
, ,
, ,
);
(),
, , ,
, ;

setUpQ.


xUnit


, , II ,
. ,
. ,
, ,
,
.
TDD.
xUnit
30 . ,
, ,
xUnit. - , , ,
xUnit
. :
. xUnit
. (Martin Fowler) :
,
.
, xUnit
.
xUnit, ,
.
.
, xUnit.
- ,
.
xUnit, ,
assert
, .
assert . - -

124

II xLlnit

xUnit assert
: GUI
.
JUnit Test,
TestCase TestSuite. ,
,
JUnit, Test
:
public interface Test {
public abstract int countTestCasesO:
public abstract void rundestResult result):
}

()

.
countTestCases()
TestResult ,
TestCases.

HI

,
.
TDD,
, , .
,
,
TDD.
,
, ,

, .

J
J

,
:
?
?
?
?

?
.
.
,
( ,
, ,
). , , ,
. , ,
.
, :
,
. :
.
:

25 ,

127

,
: .
?
. 25.1
( (Gerry Weinberg)
Quality Software Management). ,
.
,
.

-___.


25.1.

, ?
,
. ,
. , ,
. :
.
, ?
, , .
.
.
? 25.1 .
, ,
. . , . . ,
. , . ,
, .
, !
,
.
, . ,
. ,
.
,
, ? ,
. ... .

, ( -

128

III

,
). :
, ,
, (
)? ,
. ,
. . , .
.
. ,
(
).
, ,
, :
. . .
... ,
,
. , ,
.

(Isolated Test)

? .
,
. (,
, !)
.
. ,
(
, ,
,
). ,
, ,
. , ,
,
. (, !)
, , .

.
,
, .
, . -,
,
.

25 ,

129

, - . -, ,
.
, ,
,
, .
,
,
. :
: ,
.
,
.
, .
,
.
. , ,
. , ,
.
, ,
. , ,
, ,
.
,
.
,

. , ,
. ,

,
.
, , ,
, ,
.
, .

(Test List)
? ,
, .
, :
, ,
. , ,
.
5

297

130

III


. ,
. ,
, .
, ,
. , ,
. ,
, .
.

, .
,
.
. ,
,
. , ,
. ,
, ( )
.
, ,
, .
, . ,
, , -
. , ,
, .
,
? ,
. -,
, .
, . , , , ,
, .
, ,
(,
, , ,
.) ,
,
.
,
. -, ,
. ,
.
, .
,
.

" ^^^^

25 ,

131

, ,
, , .
.
, ,
.
. ,
,
. TDD
:
.
,
.
. .
.
<> . ,
.
,
.
,
.
,
, .
,
. , ,
, , .

(Test First)
?
.
. ,
.
, .

( - ):
; ( )
; , ,
.
. ,
. , ,
. .
, ?
?
: -

132

III

,
, , ,

.
, , ,
. ,
, ,
, - -
.

. , ,
.

assert (Assert First)


assert1? .
?
? 2
, ,
.
?
, ,
.
? assert,
.
(Jim Newkirk).
assert,
. ,
, , .
?
?
? ,
?
?
?

?
assert , . ,
assert . . . .
(user stories). :
. . .

25 ,

133

?
, ?
, ,
, .
:
?
?
. ,
. . ,
abc.
testCompleteTransactionO {
assertTrue(reader.isClosedO);
assertEqualsC'abc", reply.contentsO);
}

reply? , :
testCompleteTransactionO {
Buffer reply= reader.contentsO:
assertTrue(reader.isClosed());
assertEqualsC'abc", reply.contentsO);
}

? , :
testCompleteTransactionO {
Socket reader= Socket ("local host", defaultPortO):
Buffer reply= reader.contentsO:
assertTrue(reader.i sClosed()):
assertEqualsC'abc". reply.contentsO):
}

:
testCompleteTransactionO {
Server writer= Server(defaultPort(). "abc"):
Socket reader= SocketC'localhost". defaultPortO);
Buffer reply= reader.contentsO;
assertTrue( reader. i sCl osedO):
assertEqualsC'abc". reply.contentsO);
}


,
, .
assert.

134

III

(Test Data)
?
, . ,
.
,
. ,
.
1 2, 1.
,
, , .

,
.

. ,
pJus(),
2 + 2 . ,
: 1 + 1
. ,
, .
2 + 2 , ,
. , ,
plus()
( ,
, ,
plus() , ,
). ,
2 3
( 3 + 4
Smalltalk).
Test Data ( ) Realistic
Data ( ),
.
:
,
,
;

( );
,
, ,
, ,
.

25 ,

135

(Evident Data)
?

.
, . , -
: ,
?
, ,
.
. ,
1,5 . ,
(USD) (GBP).
2:1. $100,
50 GBP - 1,5 % =* 49,25 GBP. :
Bank bank= new Bank().
bank.addRate("USD". "GBP", STANDARD_RATE);
bank.commission(STANDARD_COMMISSION);
Money result= bank.convert(new NotedOO, "USD"), "GBP");
assertEquals(new Note(49.25. "GBP"), r e s u l t ) :


:
Bank bank= new BankO:
bank.addRate("USD". "GBP". 2):
bank.commission(0.015);
Money result= bank.convert(new NotedOO, "USD"). "GBP");
assertEquals(new NotedOO / 2 * (1 - 0.015), "GBP"), result);

,
, .
Evident Data ( ) :
.
assert, ,
. ,
.
Fake It ( ) ,
, .
Evident Data ( ) ,
, . ,
.
,
.

, ,
, .

One Step Test ( )


?
, , -, -, -,
.

. ,
:
;
;
;
;
;
;
;
;
;
;
-.
. , ,
, , ,
, , .
, ,
,
, .
, : , ,
, ,

26

137

, , ? ! ! ,
. . ,
, , ,
.
, ,
( ), ,
. , ,
( ),

.
,
. -,
.
, ,
(Growth) : ,
. -, ,
, .
, ,
.
,
.

Starter Test ( )
?
, -
, .
,
: ? ,
, ,
. ,
. , ,

.
,
:
?
?
?
,
. ,
.
.

138

III

?
. :

,
,
. 3 + 4 = 7. .
,
, ,
(),
. ,
.
, (),
.
, ,
, ?
Starter Test ( ), :
J , .
.
. ,
.
:
Reducer r= new Reducer(new PolygonO);
assertEqua1s(0. reducer.result().npoi nts);

! .
...
One Step
Test ( ): - ,
,
. ,
.
, .
- ,
, .
,
. , . :
StartServer
Socket= new Socket
Message= "hello"
Socket.write(message)
AssertEquals(message. socket.read)

: ,
..>

26

139

Explanation Test ( )

?
, - .
, TDD/
. ,
TDD,
, , ,
.
. ,
.
.
,
. TDD
, .
, ,
.
?
: -, , Foo
, , , 76?
, : .
Foo , , 76.
Foo , ,
67.
. -

,
. ,
.

Learning Test ( )1
,
?
.
, ,
Mobile Information Device Profile (MIDP)
Java. RecordStore
. ,
? .
1

(Jim Newkirk) (Laurent Bossavit) ,


.

140

III

. ,
.
, ,
, API , .
, :
RecordStore store;
public void setup {
store= RecordStore.openRecordStore("testing", true):
}
public void tearDownO {
RecordStore.deleteRecordStore("testi ng");
}

'

public void testStoreO {


int id= store.addRecord(new byte[] {5, 6}. 0, 2):
assertEquals(2, store.getRecordSize(id));
byte[] buffer= new byte[2];
assertEqual.s(2, store.getRecord(id, buffer, 0));
assertEquals(5, buffer[0]);
assertEqua!s(6. buffer[l]);
}

API , ,
.
,
.
,
. .
, ,
. ,
.

Another Test ( )
?
, ,
.
( ,
, , ).
,
.
, , , ,
, . ,
!

26

141

,
.
. , .
.
(, , )
.
, ,

. , ,
.
, .

Regression Test ( )
, ?
, ,
.
,
, .
, ,
,
, , .
.

, .

. ,
, ,

.
, ,
. , ,
: .

Break ()
, ?
.
, .
, , ,
, .
,
. , - ,
: -

142

I I I

! . .
.
, , , ,
.
-
? , , ?
, ?
(Dave Ungar) (Shower Metho
dology). , , . , ,
, , .
,
, . TDD
. ,
, (Obvious Implementation).
, , (Fake It).
- , (Triangulate). -
, , , , .
. 26.1 , .
. ,
, .
.


. 26.1. ,

,
.

.
,
.

.
-.
, .
( ,
.)
,
. -

26

143

.
,
. ,
,
, .
. ,
, , , ,
, .
:
, ,
. - .
, , ,
. ,
.

Do over ( )
, ? .
. . .
. . ,
, ,
, . ,
, 20 ,
.
, .
. . ,
.
, .

. 25 ,
.
(Tim
Mackinnon).
. ,
.
. ,
,
.
,
.
, ,
, , . ,
: , , - v
-?

144

III

Cheap Desk, Nice Chair ( ,


)
TDD?
, . .
, .
, , $100 000
, , $10 000
.
,
, ,
. , ,
,
. ,
.
, , .
,
. ,
:
,
, .
(Manfred Lange) ,

. //
,
.

(Child Test)
, ?
,
. . .

. ,
, .
: ,
, ,
. .
.
, , ,
, . ?
, ?
, .
,
. , , ,
.
,
, ,
. ( , .
, ! ---! ! ,
.
,
. , , .
,
.)

146

III

. .
, , , .
.

Mock Object ( )
,
? ,
.
.
, 1.
.
. ,
,
, ,
,
. ,
.
,
.
, ,
.
public void testOrdertookupO {
Database db= new MockDatabaseO;
db.expectQueryCselect orderjio from Order where custjio is 123");
db.returnResult(new String[] {"Order 2" ."Order 3"}):
}

MockDatabase ,
. , ,
(result set),
.

: .
, ,
, 14 . ,
, 14 , ,
.
,
(
Singleton ()). ,
, www.mockobjects.com.

27

147

, ,
, .
.
(Massimo Arnoldi) ,
, .
,
.
,
, ( ,
, ) Exchange (
) ,
. , .
,
.

, .
,
, ? ,
,
, ,
.

Self Shunt ()
,
?
, .
, ,
.
TestResult, ,
, , , ,
, , . ,
, .
:
ResultListenerTest

def testNotification(self):
result= TestResultO
listener= ResultListenerO
result.addListener(1istener)
WasRun("testMethod").run(result)
' assert 1 == listener.count

, :
ResultListener
class ResultListener:

148

III
def

i n i t (self):
self.count= 0
def startTest(self):
self.count= self.count + 1

-! Listener?
TestCase. TestCase
(Mock Object).
ResultListenerTest
def testNotification(self):
self.count= 0
result= TestResultO
result.addLi stener(self)
WasRunC"testMethod").run(result)
assert 1 == self.count
def startTest(self):
self.count= self.count + 1

, Self Shunt (
), , , , .
. 0, 1.
.
1? , - startTest().
startTest()?
.
count ,
count 0
1 .
, Self Shunt ()
Extract Interface ( ),
,
. , :
.
, ,
, ,
.
Self Shunt ()
, Java
.
,
. Java
, .
.
,
, ,
.

27

149

Log String (-)


,
? , ,
-
.
, 20,
TestCase.
(Template Method), , ,
setUp(), , tearDown().
,
. ,
-. , :
def testTemplateMethod(self):
test= WasRunC'testMethod")
result= TestResultO
test.run(result)
assertC'setllp testMethod tearDown " == test.log)

:
WasRun
def setUp(self):
self.log= "setUp "
def testMethod(self):
self.log= self.log + "testMethod "
def tearDown(self):
self.log= self.log + "tearDown "

Log String (-) ,


Observer ()
. ,
, ,
(Set) ,
assert .
Log String (-) Self Shunt
(). -
, -,
.

Crush Test Dummy (


)
,
, ?
, .

150

III

.
? ? ,
, .
, ,
, .
? .
. Fake
It ().
File:
private class FullFile extends File {
public FullFileC'String path) {
super(path);
}
public boolean createNewFileO throws IOException {
throw new IOExceptionO;
}
}

:
public void testFileSystemErrorO {
File f= new FullFileC'foo");
try {
saveAs(f):
failO:
} catch (IOException e) {
}
}

Mock Object ( ),
.
(inner)
Java. ,
,
:
public void testFileSystemErrorO {
File f= new FileC'foo") {
public boolean createNewFileO throws IOException {
throw new IOExceptionO;
}
}:
try {
saveAs(f):
failO:
} catch (IOException e) {
.
}
}

'

27

151

Broken Test ( )
,
? .
(Richard Gabriel).
. ,
, , .
, . ,
,
,
, , ,
, ,
, .

.
, , .
, , .
, ,
. ,

.
,
. . ,
, .

,
, .

Clean Check-in ( )
( ,
? .
'
? .
(Bubba Whitman), Walt's stevedore brother
, ,
. ,
, ,
, .
, , ,
. ( ,

.
, , ,
.)

152

III

, ,
,
. ( ,
,
.) , ,
?

. ,
, ,
. ,
, ,
,
.
.
, :
. ,
:
,
. , - ,

.

, .
,
, .
,
( ).

Fake It ()
, ?
, .
,
.

xUnit. :
return "1 run, 0 failed"

:
return "%6 run. failed" % self.runCount

. :
return "%6 run. %6 failed" % (self.runCount . self failureCount)

Fake It () ,
, .
i ( ,
). ,
, (
, ,
).
Fake It () -5 . , ?

154

III

, - ,
, ,
. (Peter Hansen)
:
TDD
. ,
, ,
. , ,
.
,
? , ,
. ,
, . ,
.
Fake It ()
:
. ,
, .
, , .
.
.
.
,
.

.
, , .
Fake It () ,
, ? ,
. ,
1:
assertEquals(new MyDate("28.2.02"). new MyDate("1.3.02").yesterdayO);
MyDate
public MyDate yesterday0 {

return new MyDate("28.2.02");


}
'

.
:
MyDate
public MyDate yesterdayO {

(Dierk K6nig) .

28

155

return new MyDate(new MyDate("1.3.02").days()-l);


}

- . ,
MyDate("1.3.02") this ( ). :
MyDate
public MyDate yesterday() {
return new MyOate(this.daysO-l);
}

'


,
(Triangulation) ,
. , Fake It ()
Obvious Implementation ( ).

Triangulate ()

? ,
.
. , ,
. :
public void testSumO {
assertEquals(4, plus(3. D ) :
}
private int plusO'nt augend, int addend) {
return 4;
}

,
:
public void testSumO {
assertEquals(4. plus(3. D ) :
assertEquals(7. plus(3.4)):
}

, ,
plus() :
private int pi us(int augend, int addend) {
return augend + addend;
}

,
. Fake It ()
.
, .

156

I I I

,
. assert
plus(),
assert, . ,
, pius() ,
.
assert.
, ,
.
Fake It () Obvious Implementation (
).

Obvious Implementation ( )
?
.
Fake It () Triangulate ()
. ,
. ! , . ,
Fake It () -
, plus()? , .
.
,
.
Fake It () Triangulate ()
. , ,
, . ,
, 1.
. ,
, ,
?
? ! .
: , (clean code that
works).
, .
,
.
Obvious Implementation ( )
, .
: ,
. , .
. - . -
(Laurent Bossavit) .

28

157

...
/ .

. .
, ,
.

One to Many ( )
, ?
,
.
, , ,
. :
public void testSumO {
assertEquals(5. sum(5)):
}
private int sumdnt value) {
return value;
}

( sum() TestCase,
.)
sum(new int[] {5, 7}).
sum() , :
public void testSumO {
assertEquals(5. sum(5, new i n t [ ] {5})):
}
private int sumdnt value, i n t [ ] values) {
return value;
}

Isolate
Change ( ). ,
, .
:
private int sum(int value, i n t [ ] values) {
int sum= 0:
for (int i= 0; i<values.length: i++)
sum + values[i]:

return sum:
}
:
public void testSumO {
assertEquals(5, sum(new i n t [ ] {5}));
}

158

III

private i n t sum(int[] values) {


int sum= 0;

for (int i= 0; i<values.length; i++)


sum += values[i]:
return sum;
}

Isolate Change (
). , .
, :
public void testSumO {
assertEquals(12, sum(new i n t [ ] {5. 7 } ) ) ;


xLlnit

> ,
xUnit.

Assertion
, ?
, ,
.
, ,
,
,
\ . , ' , .
, :
:
(True) , ,
, ;

- assert().
assertTrue(rectangle.area() != 0).
, ()
.
. 50, :
assertTrue(rectangle.area() == 50). xUnit
assert() ().
,
assertEqualsO ,
. , ,
, assertEqualsO
. ,

160

III

, . ,
JUnit : assertEquals(50, rectangle.area()).
, , . ,
Contract, status,
Offered, Running.
, :
Contract contract= new Contract!): // Offered
contract.begin();
// Running
assertEquals(Running.class, contract.status.class):

status.
, status
. , status Running,
:
assertEquals(. . .. contract.startDataO): / / ,
// status Offered

, , ,
(public)
. JXUnit,
JUnit , ,
.

, . ,
- ,
,
.
, . ,
, .
, ,
, , ,
.
xUnit Smalltalk ( SUnit)
assert ,
, .
Java ,
Java ,
assert() .
, assert() ,
.
JUnit 1.
, ' True", false) ,
,
,
.

29 xUnit

161

: Assertion failed:
True. ,
.
, assert()
.
, ,
.

Fixture1 ()
,
?
- TestCase. setUp()
( ).
(
) , ? .
: ,
, ,
. , ,
. (
scaffolding , ).
. :

,
,
,
;
,
(
).

. '
assert(),
.
, , ,
, , ,
.
xUnit . ,
,
.
setUp().
, .
fixture
. . .
6 *. 297

162

III

, ,
, ,
. :
EmptyRectangleTest
public void testEmptyO {
Rectangle empty= new RectangleCO,0.0,0):
assertTrue(empty.isEmpty());
}
public void testWidthO {
Rectangle empty= new Rectangle(0,0,0.0):
assertEquals(0.0. empty.getWidthO, 0.0):
}

( assertEquals()
,
.) , :
EmptyRectangleTest
private Rectangle empty:
public void setUpO {
empty= new RectangleCO.0,0,0);
}
public void testEmptyO {
assertTrue(empty. isEmptyO):
}
public void testWidthO {
assertEquals(0.0. empty.getWidthO. 0.0);
}

. xUnit ,
setUp() TestCase
. ,
, ,
setUp() , .
?
.
setUpO, . , ,
, . ,
, ,
.
TestCase
xUnit.
TestCase.
,
, .

29 xUnit

163

,
(Rectangle), ,
, , NormalRectangleTest.
setUp(), Rectangle, . Rectangle
. ,
, TestCase.
,
() . (
).
. \ ,
. ,
- .

External Fixture ( )
?
tearDown() ,
.
,
, , . ,
, ,
, . :
testMethod(self):
file File("foobar").open()
try:
... ...
finally:
file.closeO

,
:
setUp(self):
s e l f . f i l e - File("foobar").open()
testMethod(self):
try:
... ...
finally:
self .file.closeO

-, finally
, - . -,
,
finally . ,

164

III

try, finally close,


.
xUnit , tearDown()
. tearDown()
, (
, setUp(),
tearDown() ).
:
setUp(self):
s e l f . f i l e = File("foobar").open()
testMethod(self):

... ...
tear-Down (self):
self.file.closeO

Test Method ( )
? , test.
,
, ?
-
:
( Java , -, package);
;
.
, ,
.
, ,
. ,
, . ,
, .
xUnit ,
test.

(TestSuite).
, . ,
, JUnit,
testAssertPosInfinityNotEqualsNeglnfinity. ,
, , , ,
, assert() JUnit

. , JUnit,
, ,

29 xUnit

165

. (
).

. ,
, .
,
. , ,
(, ,
). , ,
.
(Patrick Logan) ,
. (McConnell)1,
(Caine) (Gordon)2:

(outlines) . .
,
, . :
/ * 3 * /
/ * * /
/ * */

,
. ,
:
/* */
/ * */
/ * * * * /
/ * * **/
/ * * * * /
/ * */

, - .
, .
.
, , ,
, ,
. ( - Java,
Eiffel.)

.
1
2

McConnell, Steve. 1993. Code Complete, chapter 4. Seattle, Washington: Microsoft Press.
Caine, S. H., Gordon, E. K. 1975. PDL: A Tool for Software Design, AFIPS Proceedings of the 1975
National Computer Conference.
tuple
. . .

166

III

Exception Test ( )
?
, ,
.
, , .
, .
:
public void testRateO {
exchange.addRate("USD". "GBP". 2);
i n t rate= exchange.findRate("USD". "GBP"):
assertEquals(2, rate);

'

}
.
:
public void testMissingRateO {
try {
exchange.findRate("USD". "GBP"):
failO:
} catch (IllegalArgumentException expected) {
}
}

findRate() ,
fail() xUnit, , .
, ,
findRate(). ,
- ( ) (
assert), .

All Tests ( )
? ,
,
(package) , .
, TestCase
. , ,
. (
TDD ,
, , , ,
.) ,
xUnit, IDE,
,
AIITests, suite(),
TestSuite. AIITests :

29 xLlnit

167

public class AllTests {


public static void main(String[] args) {
juni t.swi ngui.TestRunner.run(Al1 Tests.class):
}

public static Test suiteO {


TestSuite result= new TestSuiteC'TFD tests");
result.addTestSuite(MoneyTest.class):
result.addTestSui te(ExchangeTest.class);
result. addTestSuiteddentityRateTest. class):
return result;
}
}
AIITests() main(),
IDE .

? ,
, ,
, , ,
, ,
1. ,
( ) ,
,
.

,
,
.
(design patterns)
, - 2.
Design Patterns ( ) ,

. ?
, ,
. TDD
.
.
, , .

.
:
1
2

Alexander Christopher. 1970. Noteson the Synthesis of Form. Cambridge* MA: Harvard University Press.
Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John. 1995. Design Patterns: Elements of
Reusable Object Oriented Software. Boston: Addisoh-Wesley. : ., .,
., . - .
. .: , 2001.

30

169

Command ()
, ;
Value Object (-)
, ,
(aliasing);
Null Object (-)
;
Template Method ( )
, ,
.
Pluggable Object ( )

;
Pluggable Selector ( )

;
Factory Method ( )
;
Imposter ()
;
Composite () ,
;
Collecting Parameter ( ) ,
, ,
, ,
.
. 30.1 , TDD
.
3 0 . 1 . ,
(TDD)

Command ()

/
/

Value Object (-)

Template Method ( )

/
/

Pluggable Object ( )

Null Object (-)

Pluggable Selector ( )
Factory Method ( )
Imposter ()
Composite ()
Collecting Parameter ( )

/
/
/

170

III

Command ()
,
, ? ,
, .
.
,
(,
).
.
, , ,
.
(, -),
, . ,
, .
, , ,
, .
,
.

.
.
, .
. ,
.
. ,
, run().

Runnable Java:
Runnable
interface Runnable
public abstract void run();

run() , .
, Java
Runnable ,
, ( -
Smalltalk/Ruby LISP).

Value Object (-)


,
, ?
.

30

171


.
- . ,
.
.
(, , - ,
).
, , (Rectangle).
, ,
. (, )
.
, .
, ,
! , ,
.
(aliasing).

, , ,
.
. -
, , .
.

, , ,
,
. Observer ().
, ,
, , . ,
, -
. Observer ()
, ,

.
,
. ,
. ? ,
. ,
. , ,
,
.
,
.
, , 1
Smalltalk. 2 1,
?

172

III

:- 2.
b := a.
:= a bitAt: 2 put: 1.

=> 6
b => 2

, . Small
talk
, .
,
. .
Value Object (-)
, .
, -.
( ). , -

.
,
:
, ,
, ,
.
Value Object (-) ,
, , . ,
, ,
,
. , Value Object (-)
- , ,
' .
- (
,
).
, , .
, ,
.

Null Object (-)


?
, .
,
, .
, java.io.File:
java.io.File
public boolean setReadOnlyO {
SecurityManager guard = System.getSecurityManagerO:

30 1 7 3
if (guard != null) {
guard.canWrite(path);
}
return fi1eSystem.setReadOnly(thi s);
}

java.io.File 18 guard != null.


, Java
,
. Sun ,
getSecurityManager()
null?
LaxSecurity,
:
LaxSecurity
public void canWrite(String path) {
}

- SecurityManager,
, LaxSecurity:
SecurityManager
public static SecurityManager getSecurityManagerO {
return security null ? new LaxSecurityO : security:
}

, -
null.
:
File
public boolean setReadOnlyO {
SecurityManager security - System.getSecurityManagerO;
security.canWrite(path);
return fileSystem.setReadOnly(this);
}

OOPSLA
(Erich Gamma) , Null Object (-)
JHotDraw.
, ,
,
. (
.)

Template Method ( )

,
? ,
.

174

I I I


:
;
;
.


.
-

. ,
.
, . ,
JUnit :
TestCase

public void runBareO throws Throwable {


setUpO:
try {
runTestO:
}
finally {
tearDownO;
, }
}

, TestCase, setUp(), runTest() tearDown()


, .
Template Method ( ) :
? TestCase.runBare()
:
setUp() tearDown() ;
runTest()
, -.
,
, ,
.
Java - ;
Smalltalk ,
SubclassResponsibility.
,
. ,
. , : ,
, , ,
,
.

30

175

,
.
, , ,
.
.

Pluggable Object ( )
?
:
if (circle) then {
. . .. circle ...
} else {
. . . . circle
}


. (circle)
,
,
, . .
TDD ,
.
,
, , -
: PluggableObject ( ).

.
. ,
. - -
, , ,
.
, ,
,
.
- , , ,
,
,
.
:
SelectionTool
Figure selected:
public void mouseDownO {
selected= findFigureO;
i f (selected ! null)

176

III

select(selected);
}
public void mouseMoveO {
if (selected != null)
move(selected):
else
mqveSelecti onRectangl e();
}
public void mouseUpO {
i f (selected == null)
selectAllO:
}

( ,
, ). , ?
, SelectionMode, : SingleSelection
MultipleSelection.
SelectionTool
SelectionMode mode:
public void mouseDownO {
selected= findFigureO;
i f (selected != null)
mode= SingleSelection(selected);
else
mode= MultipleSelectionO;
}
public void mouseMoveO {
mode. mouseMoveO;
}
public void mouseUpO {
mode.mouseUpO;
}


( ) .

Pluggable Selector1 (
)

?
.
Beck, . 1997. The Smalltalk Best Practice Patterns,
70-73. Englewood-Cliffs, NJ: Prentice-Hall.
, , (Phyllis Diller): ,
, , .

30

177

,
? ,

.
abstract class Report {
abstract void printO:
}
class HTMLReport extends Report {
void printO { ...
}
}
class XMLReport extends Report {
void printO { ...
}
}

: switch.
.
:
;
switch;
.
abstract class Report {
String printMessage;
Report(String printMessage) {
this.printMessage= printMessage;
}
void printO {
switch (printMessage) {
case "printHTML" :
printHTMLO:
break;
case "printXML" :
printXMLO:
break:
}
}:
void printHTMLO {
, }
void printXMLO {
}
'}

, ,

switch.

178

III

Pluggable Selector ( )
Reflection API:
void printO {
Method runMethod= getClassO,getMethod(printMessage. null):
runMethod.invokeCthis. new Class[0]):
}

-
, , ,
switch.
, .
.
,
: ,
.

Factory Method ( )
,
? ,
.
, .
, , , ,
. , Java,
.

. :
public void testMultiplicationO {
Dollar five= new Dollar(5):
assertEquals(new Oollar(lO), five.times(2)):
assertEquals(new Dollar(15). five.t1mes(3)):
}

Money,
, Dollar.
,
, .
assert :
public void testMultiplicationO {
Dollar five = Money.dollar(5):
assertEquals(new Dollar(lO). five.times(2));
assertEquals(new Dollar(15). five.times(3));
} ,
Money
static Dollar dollardnt amount) {

30

179

return new Dollar(amount);


}

(Factory Method),
.
,
: , ,
, .
, ,
.
.

Imposter ()

? ,
, .
-

. , Pluggable
Selector ( ),
. ,
.
, .
, . ,
.
if
- , . ,
,
.
TDD, Imposter (
) , .
.
, . ,

:
testRectangleO {
Drawing d= new DrawingO:
d.addFigure(new RectangleFigureCO, 10. 50, 100)):
RecordingMedium brush= new RecordingMedlumO;
d.display(brush);
assertEqualsC'rectangle 0 10 50 100\n", brush.logO);
}

.
Imppster () : RectangleFigure
OvalFigure.

180

I I I

testOvaK) {
Drawing d= new DrawingO;
d.addFigure(new OvalFigure(0, 10, 50. 100));
RecordingMedium brush= new RecordingMediumO;
d.display(brush);
assertEqualsC'oval 0 10 50 100\n", brush.logO):
}

, Imposter (
) , .
, , Money
, Money. ,
, , .
Imposter ()
:
Null Object (-)
, ;
Composite ()
.
Imposter ()
, ,
.

Composite ()
,
? Imposter
() ,
, .
: Account () Transaction
().
Composite (), .
Transaction (,
,
):
Transaction
Transaction(Money^value) {
this.value= value:
}

Accout
Transaction:
Account
Transaction transactions!;]:
Money balanceO {
Money sum= Money.zeroO:

30

181

for ( i n t 1 = 0 : i < transactions.length; i++)


sum= sum.p,lus(transactions[i].value);
return sum;

}
:
Transaction ;
Account .
. ,
. , :
OverallAccount,
Account. ! !
, Account Transaction
? Holding (),
- :
Holding
interface Holding
Money balanceO:

balanceO Transaction,
:
Transaction
Money balanceO {
return value;
}

Account , Holding:
Account

Holding holdings!!]:.
Money balanceO {
Vi
Money sum= Money.zeroO:
for (int i= 0; i < holdings.length: i++)
j sum= sum. pi us (hoi dings[i]. balanceO):
return sum:
}

, OverallAccount, .
OverallAccount Account,
, Account.
.
Composite ().
. ,
. ,
,
. ,
: (Folders),
(Folders), (TestSuites), -

182

III

(TestSuir.es), (Drawings),
(Drawings).
,
.
Composite
(), , ,
. , ,
,
,
-. ,
,
, Composite ()
, .

Collecting Parameter ( )
,
? ,
.
java.io.Externalizable. write- Ex
ternal ,
. ,
,
ObjectOutput, :
java.io.Externalizable
public interface Externalizable extends java.io.Serializable {
void writeExternal(ObjectOutput out) throws IOException:
}

-
Composite (). JUnit

TestResult ,
.
- ,
,
. , ,
Expression . ,
, , , :
testSumPrintingO {
Sum sum= new Sum(Money.dollar(5), Money.franc(7));
assertEquals("5 USD + 7 CHF", sum.toStringO);
}

String toStringO {
return augend + " + " + addend;
}

30

183

, Expression
, :
testSumPrintingO {
Sum sum= new Sum(Money.do11ar(5). Money.franc(7));assertEquals("+\n\t5 USD\n\t7 CHF", sum.toStringO);
}

-,
:
String toStringO {
IndentingStream writer= new IndentingStreamO;
toString(writer);
return writer.contentsO:
}
void toStringdndentingWriter writer) {
writer.print1n("+");
writer. indentO;
augend.toString(writer):
writer.printlnO:
addend.toString(writer);
writer.exdentO:
}

Singleton ()
,
? .
,
.


.
TDD 1 .
.
TDD ,
. , TDD , ,
, .
. ,
.
,
, .
, .
, , TDD,
: ,
. ,
. ,
,
. : , ,
,
, . .

Reconcile Differences ( )
?
.
, .
Fowler, Martin. 1999. Refadoring: Improving the Design of Existing Code. Boston: Addison-Wesley.
: . . : . .: -, 2003.

31

185

. . ,
, , .

.
, ,
, , .

.
, ,
.
,
.
:
. ,
.
. ,
.
. ,
.
. ,
.
.
,
, . ,
,
, .
, , ,
. ,
?
.
,
. .

Isolate Change ( )
,
? .
:

, , , .
.
,

186

III

,
.
, , ,
, ,
. , ,
findRate()
, findRate() .
findRate() .
.
, , ,
, .
.
Extract Method ( ),
Extract Object ( ) Method Object (
).

Migrate Data ( )
?
.

.
,
.
1. .
2. ,
.
3. ,
.
4. .
5. ,
.
API.
:
1. .
2.
, .
3. .
4. .
5. .

31

187

,
One to Many ( ). ,
TestSuite, One to Many ( ).
:
def testSuite(self):
suite= TestSuiteO
sui te.add(WasRunC"testMethod"))
suite.run(self.result)
assertC'l run. 0 failed" == self.result.summaryO)

, test:
class TestSuite:
def add(self. test):
self.test= test
def run(self. result):
self.test.run(result)

.
:
TestSuite
def i n i t (self):
self.tests [ ]

, test,
:
TestSuite
def add(self, test):
self.test- test
self.tests.append(test)

.
, ( ),
.
TestSuite
def run(self. result):
for test in self.tests:
test.run(result)

test:
TestSuite
def add(self. test):
self.tests.append(test)


, , ,
Java, Vector/Enumerator Collection/Iterator.

188

III

Extract Method ( )
?

.


. . ,
.
, :
1. , .
, ,
.
2. ,
, ,
.
3. . .
4. ,
, .
5. ,
.

Extract Method ( ),
. , . .
? ,
, ,
, - .
, ,
, .
. ( Smalltalk
Refactoring Browser :
, , , ,
,
, .)

. , , Inline Method (
), ,
.

31

189

Inline Method ( )
,
?
.

1. .
2. .
3. . ,
, reader.getNext(), ,
,
.

, ,
, Bank
Expression Money.
public void testSimpleAdditionO {
Money five= Money.dollar(5):
Expression sum= f i v e . p l u s ( f i v e ) ;
Bank bank= new BankO;
Money reduced= bank.reduce(sum, "USD"):
assertEquals(Money.dollar(10). reduced);
}

.
Money? , . ?
Bank.reduce() , :
public void testSimpleAdditionO {
Money five= Money.dollar(5);
Expression sum= f i v e . p l u s ( f i v e ) ;
Bank bank= new BankO;
Money reduced= sum.reduce(bank, "USD");
assertEquals(Money.dollar(10), reduced);
}

, , , .
, Inline Method ( )
.
,
,
. , ,
, .

190

III

,
. ( , .)
, Inline Method ( ) ,
, : ,
, ... , ?
, .
, .

Extract Interface ( )
Java?
, .

1. .
.
.
2. ,
.
3. .
.
4. , , .

,
. , Rectangle (
), Oval ()
Shape (). ,
, . ,
.
, ,
Crash Test Dummy ( )
(Mock Object).
, .

IFile, File.
,
, ? , File,
DiskFile,
, , , .

31

191

Move Method ( )
,
? , ,
.

1. .
2. . .
.
3. ,
,
.
- ,
. -
,
.
4. .

,
. ,
.
Shape ():
Shape
int width= bounds, right - bounds. l e f t O ;
int height bounds.bottom() - bounds.top();
int area= width * height;

, , , ,
,
. , ,
Shape, bounds (
Rectangle). Rectangle:
Rectangle
public int areaO {
int width= this.righto - this.leftO;
int height= this.bottomO - this.topO:
return width * height:
}

'

192

I I I

Shape
i n t area- bounds.area():

Move Method ( )
:
,
.
, , ,
.
.
. Rectangle
... ! .
.
.
, ,
. .

Method Object ( )
,
?
.

1. ,
.
2.
.
3. run().
, .
4. run()
.

-
. , ,

,
. -,

31

193

.
.
.
- ,
Extract Method ( ).
,
, ,
,
.
, ,
. -
,
, - .

Add Parameter ( )
?

1. ,
.
2. .
3. ,
, ,
.
.

.
, ,
,
.

. ,
,
.

Method Parameter to Constructor Parameter


( )
?
7 .297

194

III

1. .
2. ,
.
3. .
4. parameter this.parameter.
5. ,
.
6. this.
7. .


, API,
( ). , , ,
.


TDD

,
, TDD
. ,
. ,
, ,
.

?
:
?

?
,

. ,
,
. ?
, .
TDD , .

.
, .
,
. .
,
. 20
,
.

196

III

, .
, 20 ,
. , .
, ,
.
, , .
Refactoring
Browser for Smalltalk -
. Java
. , ,
.

?
(Phlip) ,
: , .
, .
, ,
. :
;
;
;
.
, . ,
.
( : )
.
?
() ,
,

?
, ,
.
, ,
:
.
, assert(), ,
- . ,
.

32 TDD

197

.
, ,
, .
. TDD
, , . ,
,
. , - , ,
. :
, ,
.

. , ,
. (
, 9,8 /2.
10 ,

, 10 .)
.
, ,
.
,
.
, .

TDD
?
(framework) ,
.
TDD .
: ,
.
:
, (code for today, design for tomorrow).
, TDD :
, (code for tomorrow, design for
today). :
.
,
.
,
. -

198

III

.
( , ).
,
. , ,
, ,
, .

.

/ (Open/Closed Principle1), ,
, .
, TDD
,
. , TDD ,
,
.
, ,
( ).
,
?
, .
/ (Open/Closed Principle)
, ,
, , , .
, , TDD
.
. ,
, , . ,
, . .
TDD,
, ,
.

?
? :
, .
:
- , ,
,
, - (
, ). . .

32 TDD

199

1 , ;
2 , ;
3 , .
(
), .
! ( , Small
talk, ).
(
! ! ' ).
, (Bob Binder) Testing
Object- Oriented Systems1 ( - )
65 . ?
, .
,
(MTBF, Mean Time Between Failures).
, Smalltalk ,
32- . ,
, .
, MAXINT. ,
, ,
.
,
? MTBF ?
,
, ,
, .
? ,
MTBF. ,
MTBF 10 100 , ,
(,
, - ,
).
TDD . TDD
. ,
.
- , ,
. ( -,
) . ,
:
. ,
TDD.
1

Binder, Bob. 1999. Testing Object-Oiiented Systems: Models, Patterns, and Tools. Boston: Addison-Wesle
.

200

I I I

TriangleTest
testEquilateral
self assert: (self evaluate: 2 side: 2 side: 2) = 1
testlsosceles
self assert: (self evaluate: 1 side: 2 side: 2) = 2
testScalene
self assert: (self evaluate: 2 side: 3 side: 4) = 3
testlrrational
[ s e l f evaluate: 1 side: 2 side: 3]
on: Exception

do: [:ex | A self].


self fail
testNegative
[self evaluate: -1 side: 2 side: 2]
on: Exception
do: [:ex | A self].
self fail
testStrings
[self evaluate: 'a' side: 'b' side: 'c'.]
on: Exception
do: [:ex | A self].
self fail
evaluate: aNumberl side: aNumber2 side: aNumber3
| sides |
sides := SortedCollection
with: aNumberl
with: aNumber2
with: aNumber3.
sides first <= 0 ifTrue: [self fail].
(sides at: 1) + (sides at: 2) <= (sides at: 3) ifTrue: [self fail].
A
sides asSet size

?
, ,
, ?
.
. ,

.
. ,
,
, .

32 TDD

201

, ,
, ,
.


TDD?
TDD Smalltalk Refactoring
Browser. C++ vi.
.
, TDD
(),
:
;
.
, ,
?
, TDD, ]
.
, ,
?

TDD
?
TDD
? ?
?
,
TDD, , LifeWare (www.lifeware.ch). 4 .
40 -.
250 000 250 000 (
Smalltalk).
4000 , 20 .
.
, , TDD.
,
,
.

202

III


?
,
( unit tests,
), :
, ,
, .
, , , ,
.
, (appli
cation-level tests).
( ).
, .
,
(ATDD, Application Test-Driven Development).
.
, ? ,
. ,
, , ,

.
. (
, )
: .
,
,
.
.
, ,
,
.
TDD
. , TDD
. ,
.
,
, .
One Step Test (
).
, TDD.
ATDD:
. ,
, , -

32 TDD

203

. TDD
, :
;
.

TDD
?
, ,
.
TDD. ?
TDD
( ).
.
, ,
, , .
,
, .
. , (
), ,
, , .
. .
. ?
, , :
.
,
.
, .

. , ,
,
. ,
, , ,
, .
-, . ,
, .
,
. , ,
.
, ,
. ,

204

III

, ,
, TDD.
.

TDD?

. TDD .
, - , ,
,
, TDD . TDD -
, ,
. TDD
.
,
.
, , , , .
, ,
. , 20 % .
, , 100 %
, .
, 80 % ,
.
TDD .
,
TDD ,
. ,
, TDD .
TDD ,
. ,
,
, , ,
. , ,
,
. TDD
. (
), ,
, . ,
.
,
, , TDD
.

32 TDD

205

TDD
?
, ,
.

. ,
, , ,
, .
/ ,
? - ,
, ?
TDD ,
? ( :
, ,
2 000 000
.)

TDD
?
,
, .
, , ,
, , , , .
, ,
, .
(, ! ,
, , )
. , ,
, .
? , ,
, 6 9, 6 ( 9 ).
, , ,
,
.
, , ,
.
,
. , ,
.
,
, , -

206

III

,
.
, Smalltalk Best Practice
Patterns ( Smalltalk). -
, .
, ,
, .
, ,
.

.
TDD : TDD
, . ,
Strategy
(). , .
, ,
Strategy ().
(Robert Martin) TDD.
, . ,
,
.
. , , -,
, , .

TDD ?
. , TDD
,
. ( ,
, ,
.) ?
.
, .
( -')-

. TDD,
.
TDD, . paMars Lander , 3 1999 .
1999 . ,
, - .
120 , -
. . .

32 TDD

207

, .
. .
,
. .
,
, .
. ,
? ?
. ,
TDD
, .
TDD , .
TDD ,
,
.
.
, , TDD
.
TDD,
, ,
. TDD
.
, ,
. , API
, , , ,
, .
,
, ,
.
, TDD ?,
. (Phlip) :
,
, .

,
, ,
().
, .
, ;
, .
, (
, , ,
). ,
, .

208

III

?
: Test-Driven Development .
,
.
Development ()
,
,
. TDD
, ,
, , , ,
.
Driven ( ) TDD test-first pro
gramming ( ).
fist () last ( ).
,
. .
,
, ,
. (
,
, .)
, ? ? ?
? ( ,
).
Test () ,
. , . TDD
, .
, ,
, .

TDD
?
, ,
, TDD,
, (eXtreme Programming)
. , TDD,
? ,
TDD,
, TDD
:

32 TDD

209

. , TDD,
,
. ,
,
. , ,
.
TDD. :
, ,
, . TDD
, ,
. ,

.

. ,
, , .

, , .
(Bob Martin) 1
.
, , ,
.

. ,
.
, , ,
. 15-30 .

.
(Bill Wake): 2 ,
1.

. TDD , , , ,
, ,
' .
,
.
.
. .
,
,
. ,
1

(Bob Martin) Agile Development,


. . .

210

III

. .
.
. TDD MTBF
( ), ,

. (Gareth Ree
ves) -
.
,
, ,
,
. , ,

, ,
.

TDD
(Darach Ennis) TDD,
TDD. :

TDD, . ,
TDD. :
GUI (,
Swing, CGI, JSP/Seivlets/Struts);

(, RPC, Messaging, C0RBA/EJB J MS);
TDD (,
JDBC);
,
, ,
;
TDD /
.
, , , .
TDD.


(Martin Fowler)
, ,
, ,
TDD. , (Ralph Beattie) .
,
. ,
- , . ,
, , ,
, .

, - .
, , ,
:
, . TDD
.
TDD,
. , ,
. ,
, .

. ,
, .
,
.

.
: .
Ruby,
. , ,
,
, .
Java,
Ruby. , ,
, ,
.

212

III


, , .
,
. , , ,
, , TDD,
.
.
,
. ,
. ,
:
. ,

. ,
, .
,
.
,
, ,
,
. ,

.
. , (TDD),
,
,
.
,
.
, . , , . ,
, , .
, . ,
, ,
. ,
, , .

I

.
Quality Software
Management (Gerald Weinberg), , 1:
Systems Thinking^. ,
.
:
2 .
,
. ,

,
.

, .
,
, ,

.
. . 1.1-1.3
.


. 1 . 1 . , , ,

1
2

Weinberg, Gerald. 1992. Systems Thinking. Quality Software Management. New York: Dorset House.
, ,
. . .

214

. 1.2. ,

1.3. ,

, . , .
, .
,
, ,
.


.
. ,
.
, .
. 1.4.


. 1.4.

.
,
. ,
.
, ,
. . 1.4 :

215

, ,
- .
.
, ,
.
:
,
;
,
,
;
,
.


,
,
,
. . 1.5 ,
.

-
*
. 1.5.

, ,
, , ,
.
(, ),
, .
, ,
, :
.
,
,
,
.

216

,
,
.
, ,
.

II

, ,

TDD. ,
, TDD. ,
,
TDD.
, . ,
, ,
TDD, , ,
.
, fib(O) = 0. .
public void testFibonacciO {
assertEqualsCO. fib(O)):

}
int fibdnt n) {
return 0;
}

( TestCase ,
.)
, fib(l) = 1.
public void testFibonacciO {
assertEqualsCO. f i b ( 0 ) ) ;
' assertEqualsd, f i b ( D ) :
}

assert() ,
testFibonacciOfOnelsOne.
,
. 0, :
int fib(int n) {
i f (n = 0) return 0;
return 1;
}

218

II

.
,
. assert(),
fib():
public void testFibonacciO {
i n t cases[][]- { { 0 . 0 } . { 1 . 1 } } :
for ( i n t i= 0: i < cases.length; i++)
assertEquals(cases[i][l]. f i b ( c a s e s [ i ] [ 0 ] ) ) :
}


:
public void testFibonacciO {
i n t c a s e s [ ] [ > {{0.0} .{1.1} .{2.1}}:
for ( i n t i= 0; i < cases.length; i++)
assertEquals(cases[i][l], f i b ( c a s e s [ i ] [ 0 ] ) ) :
}

, . ,
1 2.
:
public void testFibonacciO {
i n t cases[][]= { { 0 . 0 } . { 1 . 1 } . { 2 . 1 } . { 3 . 2 } } :
for ( i n t i= 0; i < cases.length; i++)
assertEquals(cases[i][l]. f i b ( c a s e s [ i ] [ 0 ] ) ) :
}

! - . (
, ):
i n t f i b ( i n t n) {
i f (n == 0) return 0:
i f (n <= 2) return 1;

return 2:
}
. 2,
1 + 1.
i n t f i b d n t n) {
i f (n == 0) return 0;
i f (n <= 2) return 1;

return 1 + 1;
}'
fib(n-l):
i n t f i b ( i n t n) {
i f (n == 0) return 0;
i f (n <= 2) return 1;
return f i b ( n - l ) + 1 ;
}

219

fib(n-2):
int fibCint n) {
if (n == 0) return 0;
if (n <= 2) return 1:
return fib(n-l) + fib(n-2):
}

.
fib(2), :
int fibCint n) {
if (n == 0) return 0:
if (n == 1) return 1:
return fib(n-l) + fib(n-2);
}

,
TDD.

Account, , 180
Add Parameter, , 193
add, , 119
addend, 72
aliasing, , 171
All Tests, , 166
AllTests, , 166
application-level test, 202
assert, 132
Assert First, assert, 132
assert, , 159
assertEquals, 159
assertion, 159
ATDD, Application Test-Driven Development, 202
augend, 72

Bank, , 69
Break, , 141
Broken Test, , 151

Child Test, , 145


Clean Check-in, , 1
clean code that works, 10
Collecting Parameter
, 119
, 182
Command, , 170
Composite, , 118, 180
copy-and-paste, 40
Crossing to Safety, 42
Crush Test Dummy,
, 149
Currency, , 18
currency, , 54
cyclomatic complexity,
, 92

D
Design Patterns, , 168
Do over, , 143

Dollar, , 17
, 24
, 50
, 30
Franc, 47
, 63

equals, , 33
, 42
, 47
Evident Data, , 135
Exception Test, , 166
Explanation Test, , 139
Expression, , 67
External Fixture, , 163
Externalizable, , 182
Extract Interface, , 190
Extract Method, , 102, 188

F
Factory Method
, 50
Factory Method, , 178
fail, , 166
Fake It, , 31, 153
File, Java, 172
Fixture, , 161
framework, , 197
Franc,
, 40
, , 50
, 39
Dollar, 47
equals, 45
, 63

I
Imposter, , 67, 179
Inline Method, , 189
Isolate Change, , 185
Isolated Test, , 128

J
java.io.Externalizable, , 182
java.io.File, , 172
Jester, 94
JHotDraw, 173
J Probe, 94
JUnit, 124, 160
_--
, 91
JXUnit, 160

L
LaxSecurity, , 173
UfeWare, 201
Log String, -, 149

Method Object, , 192


Method Parameter to Constructor Parameter, 193
MIDP, Mobile Information Device Profile, 139
Migrate Data, , 186
Mock Object, , 146
Money,
, 50
, 43
, 43
, 50
Move Method, , 191
MTBF, Mean Time Between Failures, 199

N
None, , 99
Null Object, -, 172

Observer,
, 149
Obvious Implementation,
, 31, 156
One Step Test, , 136
One to Many, , 157
Open/Closed Principle, 198

Pair, , 78
pass, , 99
Pluggable Object, , 175
Pluggable Selector,
, 176
Python, 97

R
Realistic Data, , 134
Reconcile Differences,
, 184

221'

reduce, , 68
Reflection API, 178
Regression Test, , 141
Runnable, , 170

s
scaffolding, 161
Self Shunt, , 147
self, , 122
setUp, , 104, 161
Shower Methodology, , 142
SmallLint for Smalltalk, 90
Starter Test, , 137
suite, , 166
Sum, , 68
, 72
, 72
summary, , 113
SUnit, Smalltalk Unit, 160

T
TDD
, 201

, 195
, 195
, 204
, 90
, 89
, 203
, 201
, 11
, 195
, 205
, 205
, 203
, 210
, 40
, 208
, 37
, 208
, 56
, 206
, 85
tearDown, , 108, 163
Template Method, , 173
Test Data, , 134
Test First, , 131
test infected, 13
Test List, , 129
Test Method, , 164
Test, , 124
test, , 164
TestCase, , 101
Test-Driven Development
, 208

222

testFailed, , 115
test-first programming, 208
TestResult, , 113
testStarted, , 115
TestSuite, , 118, 166, 187
times, , 24
, 49, 58
toString, , 60
Transaction, , 180
Triangulate, , 155
Triangulation, , 33

v-w
Value Object, -, 32, 170
WasRun, , 99
WyCash, 17

XP, extreme Programming


TDD, 208
xUnit, 123
, 112
, 159
, 97
, 123

-
, 85

, 146
, 68
, 67
, 91

, 53
, 68
, 76
, 81
, 61

, 67
, , 90
, 131
,475
, 176
, 189
, 190
, 102, 188, 220
, 67, 90

, 183
, 175

, 186
, 134

, 89

, 94
, 206
, 141
, 213
, 214
, 193

, 66
, 71
, 81
,47
, 26

-
, 108, 149
, 89
, 90
, 26
, 196
, 23

; 185
, 128
, 139

, 60
, 151

, 190
, 197

, 150
, 112
, 166

, 93, 196


, 163

, 92
,92
, 44, 60
, 170
, 118, 180
, 178


, 140
, 24
, , 90
, 68
, 77

TDD, 144
, 90

, 189
, 188
, 191
, 192
, 68
, 164
, 178
, 173
, 192
, 142
, 186
, 22

, 118, 166
, 119
, 171
, 205
, 137
-, 172


, 149
, 214

, 175
-, 172
, 171
, 178
, 161
-, 32, 170
, 139
, 139
, 139
, 183
, 157
, 143
, 31, 156

, 77, 193
, 182
, 193

, 209
, 168, 205
Ail Tests, 166
Another Test, 140
Assert First, 132
Break, 141
Broken Test, 151
Cheap Desk, Nice Chair, 144
Child Test, 145
Clean Check-in, 151
Collecting Parameter, 119, 182
Command, 170
Composite, 118, 180
Crush Test Dummy, 149
Do over, 143
Evident Data, 135
Exception Test, 166
Explanation Test, 139
External Fixture, 163
Factory Method, 178
v
Fake It, 31, 153
Fixture, 161
Imposter, 179
Isolated Test, 128
Learning Test, 139
Log String, 149
Mock Object, 146
Null Object, 172
Obvious Implementation, 31, 156
One Step Test, 136
One to Many, 157
Pluggable Object, 175
Pluggable Selector, 176
Realistic Data, 134
Regression Test, 141
Self Shunt, 147
Singleton, 183
Starter Test, 137
Template Method, 173
Test First, 131
Test List, 129
Test Method, 164
Triangulate, 155
Triangulation, 33
Value Object, 32, 170
, 168

TDD, 205
, 191
, 141
, 31, 153
, 146

, 63
, 53
, 94

223

224

, 149
/, 198
, 209

, 32
, 134
, 141

, 163
, 89
xUnit, 123
, 184, 209
Add Parameter, 193
Extract Interface, 190
Extract Method, 102, 188, 220
Inline Method, 189
Isolate Change, 185
Method Object, 192
Method Parameter to Constructor Param, 193
Migrate Data, 186
Move Method, 191
Reconcile Differences, 184
, 196
, 109

, 67, 179
, 147
, 81
, 133

, 23, 129
, 66
, 165
, 129
, 201

, 165
, 126
, 126
, 105
, 131
, 137
, 136
, 134
, 139
, 145
, 166
, 105, 128
, 164
, 93, 196
, 94, 198

()
, 39
, 202
, 118
, 36
, 129
, 136
, 112
, 205
, 145
, 141
, 98
, 151
, 129
, 200
' , 202
, 126
, 146
, 27
, 203
, 166
, 196
, 145
, 139
, 164

, 196

, 198
, 33, 155


, 175

, 50, 178
, 217
, 161
, 163
, 104

, 76
, 81
, 39
,47
, 39

-
, 78
, 92
, 209
, 210
, 173
, 137