Browsing Uncle Bob’s blog, I’ve found this interesting post about teaching TDD with a practical example. It tries to show the principles of TDD while implementing a bowling game. A class diagram showing the mainly concepts of the game is presented. Here it is (click on the thumbnail to see a larger image):
Some Java code of Game class is show below:
1:public class Game 2:{ 3: private int rolls[] = new int [21]; 4: private int currentRoll = 0; 5: 6: public void roll(int pins) 7: { 8: rolls[currentRoll++] = pins; 9: } 10: 11: public int score() 12: { 13: int score = 0; 14: int frameIndex = 0; 15: for(int frame = 0; frame < 10; frame++) { 16: if(isStrike(frameIndex)) { 17: score += 10 + strikeBonus(frameIndex); 18: frameIndex++; 19: } 20: else if(isSpare(frameIndex)) { 21: score += 10 + spareBonus(frameIndex); 22: frameIndex += 2; 23: } 24: else { 25: score += sumOfBallsInFrame(frameIndex); 26: frameIndex += 2; 27: } 28: } 29: return score; 30: } 31: . 32: . 33:}
The Single Responsability Principle (SRP) states “there should never be more than one reason for a class to change”. Analysing carefully the Game class, you can note it has more than one reason to change. So, this class has more than one responsability. One is to keep track of the current frame and the other is to calculate the score. It sometimes is hard to see beacuse it’s difficult to detect a bad class design concerning SRP, because SRP is about implementation, not interface, as I’ve posted before. It’s bad for a class to have two responsabilities, because they become coupled. In the real world this king of coupling doesn’t exist, so in the computational world you can’t create this coupling. If we have to change a client of the BowlingGame class who depends only on the roll() operation and this change causes the BowlingGame class too, we would have to rebuild and retest another client of the BowlingGame class who depends on the score() operation. A better design would separate these responsabilities in different classes or maybe applying Interface Segregation Principle (ISP). I’ve changed a little this class design, ending up doing this (click ont the thumbnail to see a larger image):
Notice that I have decoupled the clients from the BowlingGame in terms of interfaces. Now, those interfaces provide the clients the services they need, staying far away from the Game implementation.
I think this class design is better beacuse we have decoupled the concepts from each other concerning the whole application, separating in two interfaces. Notice the implementation of the two responsabilities still continues in the BowlingGame
class, but nobody need depend upon this class. Nobody will know it exists. The client who needs to know the bowling game score, will depend on the Scorer interface and another client who wants to register rolls will depend on the RollRegister interface. The implementation is far away from the client. I will change the code concerning this new design and I will post here later. As I’ve told, I think this design is better. Do you have an opinion or suggestion about this?












My LinkedIn
#1 by bpfurtado on September 21, 2006 - 12:36 pm
I’ve got the idea of decoupling the clients from the concrete classes (dependencies) using narrower interfaces (with a Single well defined responsibility). It’s indeed a nice Object Oriented strategy
But it’s not clear in the second diagram that now the Frame class hold not anymore a reference to the concrete type BowlingGame and yes two references to the two new created interfaces.
It in fact states that the Frame class has no dependency upon the BowlingGame class, as it was already shown in the first diagram. So what changed in terms of coupling?
#2 by rnaufal on September 25, 2006 - 12:18 pm
The client of the Bowling Game don’t have to depend upon the concrete implementation of the Game. So, if the client only wants to know the actual score of the game, it will invoke the score() operation at the Score interface. If it wanna register a roll, it will invoke the roll(int) operation at the RollRegister interface. The idea here is that the BowlingGame will expose only the operations its clients would need. In a second way, we are applying here the ISP principle, because the Game class had two responsabilities: register a roll and calculate the score. We separate this responsabilities in terms of interfaces, which clients can depend upon it. If one client of the BowlingGame only depends upon the score() operation and we have only the concrete implementation class (like in the former diagram), if I change the roll(int) operation, I would have to rebuild and redeploy the whole application. If I factor in terms of interfaces, applying ISP, it won’t happen.