The Open/Closed Principle Dojo
-
Upload
matteo-vaccari -
Category
Technology
-
view
2.515 -
download
3
description
Transcript of The Open/Closed Principle Dojo
![Page 1: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/1.jpg)
Matteo Vaccari & Antonio [email protected], [email protected]
www.xpeppers.com
XP Days Benelux 2010
(cc) Some rights reserved
The Open/Closed Principle Dojo
1
![Page 2: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/2.jpg)
The FizzBuzz Game1, 2, Fizz!, 4, Buzz!, Fizz!, 7, 8, Fizz!, Buzz!, 11, Fizz!, 13, 14, FizzBuzz!, 16, 17, Fizz!...
If the number is a multiple of 3, say “Fizz”If it is a multiple of 5, say “Buzz”
If it is a multiple of 3 and 5, say “FizzBuzz”Otherwise, just say the number.
2
![Page 3: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/3.jpg)
It’s not hard...public String say(Integer n) { if (isFizz(n) && isBuzz(n)) { return "FizzBuzz"; } if (isFizz(n)) { return "Fizz"; } if (isBuzz(n)) { return "Buzz"; } return n.toString();}
public boolean isFizz(Integer n) { return 0 == n % 3;}
// ...
3
![Page 4: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/4.jpg)
New requirement
If it is a multiple of 7, say “Bang”
4
![Page 5: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/5.jpg)
No problem!public String say(Integer n) { if (isBang(n)) { return "Bang"; } if (isFizz(n) && isBuzz(n)) { return "FizzBuzz"; } if (isFizz(n)) { return "Fizz"; } if (isBuzz(n)) { return "Buzz"; } return n.toString();}
5
![Page 6: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/6.jpg)
Wait, that’s not what I meant!
If it is a multiple of 3 and 7, say “FizzBang”If it is a multiple of 5 and 7, say “BuzzBang”
If it is a multiple of 3, 5 and 7, say “FizzBuzzBang”
6
![Page 7: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/7.jpg)
Hmmm....public String say(Integer n) { if (isFizz(n) && isBuzz(n) && isBang(n)) { return "FizzBuzzBang"; } if (isBang(n) && isBuzz(n)) { return "BuzzBang"; } if (isBang(n) && isFizz(n)) { return "FizzBang"; } if (isBang(n)) { return "Bang"; } if (isFizz(n) && isBuzz(n)) { return "FizzBuzz"; } if (isFizz(n)) { return "Fizz"; } if (isBuzz(n)) { return "Buzz"; } return n.toString();}
7
![Page 8: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/8.jpg)
Hmmm....public String say(Integer n) { if (isFizz(n) && isBuzz(n) && isBang(n)) { return "FizzBuzzBang"; } if (isBang(n) && isBuzz(n)) { return "BuzzBang"; } if (isBang(n) && isFizz(n)) { return "FizzBang"; } if (isBang(n)) { return "Bang"; } if (isFizz(n) && isBuzz(n)) { return "FizzBuzz"; } if (isFizz(n)) { return "Fizz"; } if (isBuzz(n)) { return "Buzz"; } return n.toString();}
Not so simple anymore. What is gonna happen when the customer adds a new
requirement?
8
![Page 9: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/9.jpg)
OK. Nobody told you before but...
Adding IFs is evil.
9
![Page 10: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/10.jpg)
http://pierg.wordpress.com/2009/08/05/anti-if-campaign/
easy ≠ effective
10
![Page 11: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/11.jpg)
The Open/Closed Principle
Software entities (classes, modules, functions, etc.) should be open for extension, but
closed for modification
11
![Page 12: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/12.jpg)
How do we implement features?
Starting code base Changes implementedred == code changed
(Hopefully) Code cleaned up
Starting code base Change design to makeroom for new feature
Implement feature
Usual way:
OCP:
From a slide by Dave Nicolette12
![Page 13: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/13.jpg)
When I must add functionality:
• Can I do it by changing only construction code and creating new classes?
• If I can, I rock! ➪ €€€€
• If I can’t, I refactor until I can
13
![Page 14: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/14.jpg)
Rules for the OCP dojo1. Write a failing test
2. Write a setup that builds an object (or aggregate) that makes the test pass
- Factory only creates and links, no conditionals
3. Write next failing test
4. Can you make it pass by changing factory and/or creating new classes?
- Yes: great! go back to step 3
- No: refactor until you can
14
![Page 15: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/15.jpg)
Refactoring should bring the system in a state where it's possible to implement the next test just by composing objects in the setup method
No new functionality! Current test should still fail
15
![Page 16: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/16.jpg)
First test: Say the number
Just say the number
say(1) returns “1”say(2) returns “2”
16
![Page 17: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/17.jpg)
Second test: Say “Fizz”
When a number is a multiple of 3, say “Fizz”
say(3) returns “Fizz”say(6) returns “Fizz”
17
![Page 18: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/18.jpg)
Third test: say “Buzz”
When a number is a multiple of 5, say “Buzz”
say(5) returns “Buzz”say(10) returns “Buzz”
18
![Page 19: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/19.jpg)
Fourth test: say “FizzBuzz”
When a number is a multiple of 3 and 5, say “FizzBuzz”
say(3*5) returns “FizzBuzz”
19
![Page 20: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/20.jpg)
Fifth test: say Bang
When a number is a multiple of 7, say “Bang”
say(7) returns “Bang”say(14) returns “Bang”
20
![Page 21: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/21.jpg)
Sixth, Seventh, Eighth test: say FizzBang, BuzzBang, FizzBuzzBang
say(3*7) returns “FizzBang”say(5*7) returns “BuzzBang” say(3*5*7) returns “FizzBuzzBang”
21
![Page 22: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/22.jpg)
The Bowling ScoreBy Robert Martin “Uncle Bob”
http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata
22
![Page 23: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/23.jpg)
The requirements
• Write class “Game” with two methods:
• void roll(int pins); call when the player rolls a ball. The argument is the number of pins knocked down.
• int score(); called when the game is ended. Returns the final score.
http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata23
![Page 24: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/24.jpg)
int score() { int score = 0; int currentRoll = 0; for (int frame=0; frame<10; frame++) { if (isStrike(currentRoll)) { score += 10 + sumOfTwoRolls(currentRolls+1); currentRoll++; } else if (isSpare(currentRoll)) { score += 10 + rolls[currentRolls+1]; currentRoll += 2; } else { score = sumOfTwoRolls(currentRolls); currentRoll += 2; } } return score;}
The solution
http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata24
![Page 25: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/25.jpg)
What happens next?
25
![Page 26: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/26.jpg)
A new story
To support our customers on the Mars colony,we should implement Martian BowlingThis is the same as regular bowling, except for:✴ 12 frames✴ 3 balls per frame
26
![Page 27: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/27.jpg)
int score() { int score = 0; int currentRoll = 0; for (int frame=0; frame<10; frame++) { if (isStrike(currentRoll)) { score += 10 + sumOfTwoRolls(currentRolls+1); currentRoll++; } else if (isSpare(currentRoll)) { score += 10 + rolls[currentRolls+1]; currentRoll += 2; } else { score = sumOfTwoRolls(currentRolls); currentRoll += 2; } } return score;}
27
![Page 28: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/28.jpg)
int score() { int score = 0; int currentRoll = 0; int numFrames = isMartian() ? 12 : 10; for (int frame=0; frame<numFrames; frame++) { if (isStrike(currentRoll)) { score += 10 + sumOfTwoRolls(currentRolls+1); currentRoll++; } else if (isSpare(currentRoll)) { score += 10 + rolls[currentRolls+1]; currentRoll += 2; } else if (isMartian()) { score = sumOfThreeRolls(currentRolls); currentRoll += 3; } else { score = sumOfTwoRolls(currentRolls); currentRoll += 2; } } return score;}
28
![Page 29: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/29.jpg)
And another!
The scientists on Callisto play the Callisto VariantThis is the same as regular bowling, except for:✴ As long as the last roll is 10, you may keep
rolling
This may be played with either the Terran or Martian rules
29
![Page 30: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/30.jpg)
int score() { int score = 0; int currentRoll = 0; int numFrames = isMartian() ? 12 : 10; for (int frame=0; frame<numFrames; frame++) { if (callistoVariant() && isLastFrame(frame)) { while (isStrike(currentRoll)) { score += 10 + sumOfTwoRolls(currentRolls+1); currentRoll++; } } else { if (isStrike(currentRoll)) { score += 10 + sumOfTwoRolls(currentRolls+1); currentRoll++; } } if (isSpare(currentRoll)) { score += 10 + rolls[currentRolls+1]; currentRoll += 2; } else if (isMartian()) { score = sumOfThreeRolls(currentRolls); currentRoll += 3; } else { score = sumOfTwoRolls(currentRolls); currentRoll += 2; } } return score;}
30
![Page 31: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/31.jpg)
Meanwhile, on Venus...
... people play Venusian Bowling, where the number of pins is variable. It starts with 1 and increases by one until frame 11
31
![Page 32: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/32.jpg)
int score() { int score = 0; int currentRoll = 0; int numFrames = isMartian() ? 12 : (isVenusian() ? 11 : 10); for (int frame=0; frame<numFrames; frame++) { if (callistoVariant() && isLastFrame(frame)) { while (isStrike(currentRoll, frame)) { score += 10 + sumOfTwoRolls(currentRolls+1); currentRoll++; } } else { if (isStrike(currentRoll)) { score += 10 + sumOfTwoRolls(currentRolls+1); currentRoll++; } } if (isSpare(currentRoll)) { score += 10 + rolls[currentRolls+1]; currentRoll += 2; } else if (isMartian()) { score = sumOfThreeRolls(currentRolls); currentRoll += 3; } else { score = sumOfTwoRolls(currentRolls); currentRoll += 2; } } return score;}
boolean isStrike(int currentRoll, int frame) { if (isVenusian()) { return rolls[currentRoll] == frame; } return rolls[currentRoll] == 10;}
32
![Page 33: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/33.jpg)
Help!
33
![Page 34: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/34.jpg)
Another way?
int terranScore() { int score = 0; int currentRoll = 0; for (int frame=0; frame<10; frame++) { if (isStrike(currentRoll)) { score += 10 +
sumOfTwoRolls(currentRolls+1); currentRoll++; } else if (isSpare(currentRoll)) { score += 10 + rolls[currentRolls+1]; currentRoll += 2; } else { score = sumOfTwoRolls(currentRolls); currentRoll += 2; } } return score;}
int martianScore() { int score = 0; int currentRoll = 0; for (int frame=0; frame<12; frame++) { if (isStrike(currentRoll)) { score += 10 +
sumOfTwoRolls(currentRolls+1); currentRoll++; } else if (isSpare(currentRoll)) { score += 10 + rolls[currentRolls+1]; currentRoll += 2; } else { score = sumOfThreeRolls(currentRolls); currentRoll += 3; } } return score;}
int martianScoreWithCallistoVariant() { // ... }
int venusianScore() { // ... }
int terranScoreWithCallistoVariant() {// ... }
Duplication!!!
34
![Page 35: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/35.jpg)
The challenge
Can we implement all the various scoring rules with no IFs and
without duplication?
35
![Page 36: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/36.jpg)
The Bowling Score stories
36
![Page 37: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/37.jpg)
Sum of rolls
When the player does not strike or spare, the score is the sum of the two rolls.
37
![Page 38: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/38.jpg)
Sum of rollsAcceptance Criteria
scenario 0 - all zeroes. Player rolls 0 for 20 times. The application reports score is 0.
scenario 1 - all twos. Player rolls 2 for 20 times. The application reports score is 40.
scenario 2 - up and down. Player rolls 0,1,2,3,4,5,4,3,2,1,0,1,2,3,4,5,4,3,2,1. The application reports score is 50.
38
![Page 39: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/39.jpg)
Spare
When the players knocks down all pins in two rolls, the score for that frame is 10 plus the next roll.
39
![Page 40: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/40.jpg)
Spare
Acceptance Criteria
scenario 0 - one spare. Player rolls 3, 7, 4 and then rolls 0 for 17 times. The application reports score is 10 + 4 + 4.
scenario 1 - spare in the last frame. Player rolls 0 for 18 times, then 2, 8, 3. The application reports score is 10 + 3.
40
![Page 41: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/41.jpg)
Strike
When the players knocks down all pins in one roll, the score for that frame is 10 plus the next two rolls.
41
![Page 42: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/42.jpg)
StrikeAcceptance Criteria
scenario 0 - one strike. Player rolls 10, 2, 4 and then rolls 0 for 16 times. The application reports score is 10 + 6 + 6.
scenario 1 - strike in the last frame. Player rolls 0 for 18 times, then 10, 8, 3. The application reports score is 10 + 11.
scenario 2 - perfect game Player rolls 10 for 12 times. The application reports score is 300.
42
![Page 43: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/43.jpg)
A new story
To support our customers on the Mars colony,we should implement Martian BowlingThis is the same as regular bowling, except for:✴ 12 frames✴ 3 balls per frame
43
![Page 44: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/44.jpg)
Martian Bowling
When playing Martian bowling, there are 3 balls per frame, and 12 frames.
44
![Page 45: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/45.jpg)
Martian BowlingAcceptance Criteria
scenario 0 - sum of three rolls. Player rolls 1, 2, 3 and then rolls 0 for 3 * 11 times. The application reports score is 1 + 2 + 3.
scenario 1 - martian spare. Player rolls 1, 2, 7, 3, then 0 for 2 + 3*10 times. The application reports score is 10 + 3 + 3.
scenario 2 - martian strike. Player rolls 10, then 2, 3, then 0 for 1 + 3*10 times. The application reports score is 10 + 5 + 5.
45
![Page 46: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/46.jpg)
And another!
The scientists on Callisto play the Callisto VariantThis is the same as regular bowling, except for:✴ As long as the last roll is 10, you may keep
rolling
This may be played with either the Terran or Martian rules
46
![Page 47: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/47.jpg)
Callisto Variant
As long as the last roll is 10, you may keep rolling
47
![Page 48: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/48.jpg)
Callisto Variant
Acceptance Criteria
Scenario 0 - Terran + Callisto. Player rolls 0 for 2*9 times, then 10 for 5 times. The application reports score is 10*5.
Scenario 1 - Martian + Callisto. Player rolls 0 for 3*11 times, then 10 for 7 times. The application reports score is 10*7.
48
![Page 49: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/49.jpg)
Table display
The application displays a table with results for each frame
49
![Page 50: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/50.jpg)
Table display
Acceptance Criteria
The player rolls 1, 4, 4, 5, 6, 4, 5, 5, 10, 0, 1, 7, 3, 6, 4, 10, 2, 8, 6 The application reports score is
|1 4|4 5|6 /|5 /| X|0 1|7 /|6 /| X|2/6|| 5| 14| 29| 49| 60| 61| 77| 97|117|133|
50
![Page 51: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/51.jpg)
Things to remember
• Before starting to code, refactor to make implementing the feature easier
• Before refactoring, think and plan
• Always refactor on a green bar
• If you mess up, ctrl-Z until back to green (whew!)
• Only add an extension point when a new feature requires it
51
![Page 52: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/52.jpg)
Want to know more?
http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod
http://www.antiifcampaign.com/
http://matteo.vaccari.name/blog/archives/293
This presentation can be downloaded from http://slideshare.net/xpmatteo
52
![Page 53: The Open/Closed Principle Dojo](https://reader034.fdocuments.net/reader034/viewer/2022042814/554a11acb4c9058c5d8b4b18/html5/thumbnails/53.jpg)
Grazie dell’attenzione!
Extreme Programming:development & mentoring
53