PDA

View Full Version : Test-Driven Development Sample


Ron Jeffries
11-17-2003, 09:24 AM
Adventures in C#: The Bowling Game
Ron Jeffries
11/17/2003

When I demonstrate Test-Driven Development using the Bowling Game
example, I begin by describing the problem and inviting the attendees to
do a little up front design about what objcts we may need. Then I take a
very simple approach that produces a rather simple single-class solution,
with none of the complexity we anticipated. I've been wondering how to
drive the development to cause the creation of some of the classes that
are anticipated, supposing that we might have some actual need for them.
Here's an example of doing TDD with a bit bigger "design" in mind.

http://www.xprogramming.com/xpmag/acsBowling.htm

Ron Jeffries
www.XProgramming.com
Just because we learned something new today doesn't mean we were
frickin' idiots yesterday. -- Chris Morris, possibly paraphrasing someone.

--
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.

Paul Sinnett
11-23-2003, 08:27 AM
Ron Jeffries wrote (on Yahoo groups): Bob Martin commented on comp.software.extreme-programming that he liked the procedural version better than the OO one. So do I, though I have not found that it refactors smoothly to support things like an automated bowling scoreboard. That troubles me a bit.

What's the problem?

Ron Jeffries
11-23-2003, 03:53 PM
On Sun, 23 Nov 2003 16:27:48 +0000, Paul Sinnett <paul.sinnett@btinternet.com>
wrote:
Ron Jeffries wrote (on Yahoo groups): Bob Martin commented on comp.software.extreme-programming that he liked the procedural version better than the OO one. So do I, though I have not found that it refactors smoothly to support things like an automated bowling scoreboard. That troubles me a bit.What's the problem?

XP and the agile methods generally believe that we can evolve the architecture.
If evolving the architecture is difficult in some cases, we would like to
understand when, and why.

--
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.

Paul Sinnett
11-23-2003, 05:56 PM
Ron Jeffries wrote: <paul.sinnett@btinternet.com> wrote:Ron Jeffries wrote (on Yahoo groups): Bob Martin commented on comp.software.extreme-programming that he liked the procedural version better than the OO one. So do I, though I have not found that it refactors smoothly to support things like an automated bowling scoreboard. That troubles me a bit.What's the problem? XP and the agile methods generally believe that we can evolve the architecture. If evolving the architecture is difficult in some cases, we would like to understand when, and why.

I see. But what I meant was, what is the specific problem with supporting an
automated bowling scoreboard?

Ron Jeffries
11-23-2003, 07:32 PM
On Mon, 24 Nov 2003 01:56:44 +0000, Paul Sinnett <paul.sinnett@btinternet.com>
wrote:
XP and the agile methods generally believe that we can evolve the architecture. If evolving the architecture is difficult in some cases, we would like to understand when, and why.I see. But what I meant was, what is the specific problem with supporting anautomated bowling scoreboard?

Ah, sorry, I misunderstood. My solution 2, the short procedural one, only
supports computing the total game score, by design. The automated scoring board
idea is some kind of display of each frame, which, as the game progresses,
should show the pins knocked down, any marks (strike, spare (and open)), and the
cumulative score in that frame, as it becomes available.

So to make the procedural version do that, it needs to have a different
structure, such as an array of Frame objects or the like. Evolving it to be that
way in small steps seems a bit tricky.

--
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.

Paul Sinnett
11-24-2003, 05:28 PM
Ron Jeffries wrote: Ah, sorry, I misunderstood. My solution 2, the short procedural one, only supports computing the total game score, by design. The automated scoring board idea is some kind of display of each frame, which, as the game progresses, should show the pins knocked down, any marks (strike, spare (and open)), and the cumulative score in that frame, as it becomes available. So to make the procedural version do that, it needs to have a different structure, such as an array of Frame objects or the like. Evolving it to be that way in small steps seems a bit tricky.

As I understand it your procedural program already divides the calculation
into frames. The result is the sum of frames 1 to 10. If you changed the
loop such that it was 1 to n you could calculate any frame n. You also
already calculate marks - how hard could it be to return that for frame n
along with the running total?

Ron Jeffries
11-25-2003, 03:34 AM
On Tue, 25 Nov 2003 01:28:25 +0000, Paul Sinnett <paul.sinnett@btinternet.com>
wrote:
Ron Jeffries wrote: Ah, sorry, I misunderstood. My solution 2, the short procedural one, only supports computing the total game score, by design. The automated scoring board idea is some kind of display of each frame, which, as the game progresses, should show the pins knocked down, any marks (strike, spare (and open)), and the cumulative score in that frame, as it becomes available. So to make the procedural version do that, it needs to have a different structure, such as an array of Frame objects or the like. Evolving it to be that way in small steps seems a bit tricky.As I understand it your procedural program already divides the calculationinto frames. The result is the sum of frames 1 to 10. If you changed theloop such that it was 1 to n you could calculate any frame n. You alsoalready calculate marks - how hard could it be to return that for frame nalong with the running total?

Yes. However, in the current procedural version the calculation of any frame may
only safely be attempted if all the throws necessary are present -- otherwise
the code will hurl. Of course it would be possible to look to see, or to return
some kind of Null Object, except that ints aren't really objects (compiler
writers full of fear) and so cannot be null.

So it is hard. For me at least. If you'd like to write the next article, showing
how to smoothly refactor that solution to objects, I'd welcome it. At the
moment, I'm stalled and engaged in things that are more interesting to me.

--
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.

Paul Sinnett
11-25-2003, 07:22 AM
Ron Jeffries wrote: Yes. However, in the current procedural version the calculation of any frame may only safely be attempted if all the throws necessary are present -- otherwise the code will hurl. Of course it would be possible to look to see, or to return some kind of Null Object, except that ints aren't really objects (compiler writers full of fear) and so cannot be null.

You already have access to the array wrapped by PinCount. A simple
change will stop the program from crashing:

private int PinCount(int pinPosition) {

if ( pinPosition < rolls.Count ) {

return (int) rolls[pinPosition];

} else {

return 0;
}
}
So it is hard. For me at least. If you'd like to write the next article, showing how to smoothly refactor that solution to objects, I'd welcome it. At the moment, I'm stalled and engaged in things that are more interesting to me.

If you're not interested then so be it. But I don't think you should put
too much faith in your theories unless you are willing to put them to
the test.

John Roth
11-25-2003, 08:35 AM
"Paul Sinnett" <paul.sinnett@btinternet.com> wrote in message
news:3fc37245@news.totallyobjects.com... Ron Jeffries wrote: Yes. However, in the current procedural version the calculation of any
frame may only safely be attempted if all the throws necessary are present --
otherwise the code will hurl. Of course it would be possible to look to see, or to
return some kind of Null Object, except that ints aren't really objects
(compiler writers full of fear) and so cannot be null. You already have access to the array wrapped by PinCount. A simple change will stop the program from crashing: private int PinCount(int pinPosition) { if ( pinPosition < rolls.Count ) { return (int) rolls[pinPosition]; } else { return 0; } } So it is hard. For me at least. If you'd like to write the next article,
showing how to smoothly refactor that solution to objects, I'd welcome it. At
the moment, I'm stalled and engaged in things that are more interesting to
me. If you're not interested then so be it. But I don't think you should put too much faith in your theories unless you are willing to put them to the test.

Paul, considering that we've been going through multiple implementations
of the bowling game on the XP mailing list for the last week or so, for
probably around a thousand (or more) messages, I think this comment
was totally uncalled for.

You can see the commentary on Yahoo any time you want.

John Roth

Paul Sinnett
11-25-2003, 10:35 AM
John Roth wrote: "Paul Sinnett" <paul.sinnett@btinternet.com> wrote in messageIf you're not interested then so be it. But I don't think you should puttoo much faith in your theories unless you are willing to put them tothe test. Paul, considering that we've been going through multiple implementations of the bowling game on the XP mailing list for the last week or so, for probably around a thousand (or more) messages, I think this comment was totally uncalled for.

I'm not quite sure how you're reading the above, but it isn't meant to
be offensive.

Ron Jeffries
11-25-2003, 12:09 PM
On Tue, 25 Nov 2003 15:22:51 +0000, Paul Sinnett <paul.sinnett@btinternet.com>
wrote:
Ron Jeffries wrote: Yes. However, in the current procedural version the calculation of any frame may only safely be attempted if all the throws necessary are present -- otherwise the code will hurl. Of course it would be possible to look to see, or to return some kind of Null Object, except that ints aren't really objects (compiler writers full of fear) and so cannot be null.You already have access to the array wrapped by PinCount. A simplechange will stop the program from crashing:private int PinCount(int pinPosition) { if ( pinPosition < rolls.Count ) { return (int) rolls[pinPosition]; } else { return 0; }}

Yes. But that doesn't make the program /work/ if the objective is to score
frames incrementally. That makes the loop return a value, but the value isn't
correct.
So it is hard. For me at least. If you'd like to write the next article, showing how to smoothly refactor that solution to objects, I'd welcome it. At the moment, I'm stalled and engaged in things that are more interesting to me.If you're not interested then so be it. But I don't think you should puttoo much faith in your theories unless you are willing to put them tothe test.

I don't think I understand your point here. Would you care to explain it
further?

--
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.

Ron Jeffries
11-25-2003, 12:10 PM
On Tue, 25 Nov 2003 18:35:01 +0000, Paul Sinnett <paul.sinnett@btinternet.com>
wrote:
John Roth wrote: "Paul Sinnett" <paul.sinnett@btinternet.com> wrote in messageIf you're not interested then so be it. But I don't think you should puttoo much faith in your theories unless you are willing to put them tothe test. Paul, considering that we've been going through multiple implementations of the bowling game on the XP mailing list for the last week or so, for probably around a thousand (or more) messages, I think this comment was totally uncalled for.I'm not quite sure how you're reading the above, but it isn't meant tobe offensive.

FYI, I took it as an attempt to be offensive as well. Which rather surprised me,
considering the civility of our conversation so far. I'm glad I merely requested
further explanation of the comment. ;->

--
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.

Paul Sinnett
11-25-2003, 10:24 PM
Ron Jeffries wrote: <paul.sinnett@btinternet.com> wrote: Ron Jeffries wrote: Yes. However, in the current procedural version the calculation of any frame may only safely be attempted if all the throws necessary are present -- otherwise the code will hurl. Of course it would be possible to look to see, or to return some kind of Null Object, except that ints aren't really objects (compiler writers full of fear) and so cannot be null.
You already have access to the array wrapped by PinCount. A simple change will stop the program from crashing: private int PinCount(int pinPosition) { if ( pinPosition < rolls.Count ) { return (int) rolls[pinPosition]; } else { return 0; } }
Yes. But that doesn't make the program /work/ if the objective is to score frames incrementally. That makes the loop return a value, but the value isn't correct.

I see. So we don't want the program to crash. And now it doesn't. But we
also want it to return the correct value for an incomplete frame. (At least
now we know it won't crash while we're working on it.)

But what is the correct value for an incomplete frame? Our refactored call
would look like this:

Score( 2 );

where 2 is the frame we want to score. And it returns an int value. But that
value will be wrong if we haven't added enough balls to the input array
yet. So we need some way for the routine to return a value for
NOT_YET_AVAILABLE.

The simplest solution that comes to my mind is to define NOT_YET_AVAILABLE
to be some impossible score value, such as 301 or -1. So our test would be:

AssertEquals( BowlingGame::NOT_YET_AVAILABLE, Score( 2 ) );

All we have to do is make it pass - and still pass all the other tests:

public int Score( int frameNumber ) {
int rollIndex = 0;
int total = 0;
for (int frame = 0; frame < frameNumber; frame++ ){

if ( Strike(rollIndex) ) {
if ( rollIndex+2 < rolls.Count ) {
total += 10 + PinCount(rollIndex+1) + PinCount(rollIndex+2);
}
else {
return NOT_YET_AVAILABLE;
}
}
else if ( Spare(rollIndex) ) {
if ( rollIndex+2 < rolls.Count ) {
total += 10 + PinCount(rollIndex+2);
}
else {
return NOT_YET_AVAILABLE;
}
}
else {
if ( rollIndex+1 < rolls.Count ) {
total += PinCount(rollIndex) + PinCount(rollIndex+1);
}
else {
return NOT_YET_AVAILABLE;
}
}
rollIndex += FrameSize(rollIndex);
}
return total;
}

Would this pass? I have no way to test - I don't have C#. It looks fairly
nasty though. But I think that if this works then the following should work
too. This one uses the refactoring we worked out earlier as a starting
point:

public int Score( int frameNumber ) {
int rollIndex = 0;
int total = 0;
for ( int frame = 0; frame < frameNumber; frame++ ) {

const bool mark = Strike( rollIndex ) || Spare( rollIndex );
const int scoringBalls = (mark)? 3 : 2;

if ( rollIndex + scoringBalls <= rolls.Count ) {
total += PinCount( rollIndex, scoringBalls );
}
else {
return NOT_YET_AVAILABLE;
}

rollIndex += FrameSize( rollIndex );
}
return total;
}

Of course once we have it working, whichever we choose, we should refactor
before moving on to the next test...

Paul Sinnett
11-25-2003, 10:28 PM
Ron Jeffries wrote: <paul.sinnett@btinternet.com> wrote: If you're not interested then so be it. But I don't think you should put too much faith in your theories unless you are willing to put them to the test.
I don't think I understand your point here. Would you care to explain it further?

Earlier in this thread you suggested:
XP and the agile methods generally believe that we can evolve the architecture. If evolving the architecture is difficult in some cases, we would like to understand when, and why.

You then went on to suggest that the procedural version of the bowling
problem is such a case:
So to make the procedural version do that, it needs to have a different structure, such as an array of Frame objects or the like. Evolving it to be that way in small steps seems a bit tricky.

But when I disputed your theory you dismissed my suggestions because you
were busy with other things:
So it is hard. For me at least. If you'd like to write the next article, showing how to smoothly refactor that solution to objects, I'd welcome it. At the moment, I'm stalled and engaged in things that are more interesting to me.

So I meant:

1.) It's okay for you to be unconcerned with my efforts.
2.) You should not consider this as an example of problems with evolving
architecture until you have time / interest to devote to it.

No offence was meant by either point.

Paul Sinnett
11-25-2003, 10:31 PM
Ron Jeffries wrote: FYI, I took it as an attempt to be offensive as well. Which rather surprised me, considering the civility of our conversation so far. I'm glad I merely requested further explanation of the comment. ;->

I refer you to my further explanation.

PS. On the subject of further explanation, what did you mean by "compiler
writers full of fear"?

Paul Sinnett
11-25-2003, 11:22 PM
Paul Sinnett wrote: AssertEquals( BowlingGame::NOT_YET_AVAILABLE, Score( 2 ) );

That should probably be:

AssertEquals( BowlingGame::NOT_YET_AVAILABLE, game.Score( 2 ) );

Sean Case
11-26-2003, 03:57 AM
In article <9kd7sv02gvlh9vpgq3ucsrm0s8lh3vscnd@4ax.com>,
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote:

[about treating missing throws as zero]
Yes. But that doesn't make the program /work/ if the objective is to score frames incrementally. That makes the loop return a value, but the value isn't correct.

What is the correct value in this case?

Sean Case

--
Sean Case gsc@zip.com.au

Code is an illusion. Only assertions are real.

John Roth
11-26-2003, 04:19 AM
"Sean Case" <gsc@zip.com.au> wrote in message
news:gsc-4E03C1.22573426112003@nasal.pacific.net.au... In article <9kd7sv02gvlh9vpgq3ucsrm0s8lh3vscnd@4ax.com>, Ron Jeffries <ronjeffries@REMOVEacm.org> wrote: [about treating missing throws as zero] Yes. But that doesn't make the program /work/ if the objective is to
score frames incrementally. That makes the loop return a value, but the value
isn't correct. What is the correct value in this case?

None (Nill, Null, NaN, etc.) It's an error condition: the frame can't
be scored yet. What the actual return should be depends on what the
application needs it to be. The only thing we can say for sure is that
zero isn't correct: zero is a possible score so an error return shouldn't
alias it.

John Roth Sean Case

Ron Jeffries
11-26-2003, 11:02 AM
On Wed, 26 Nov 2003 06:31:19 +0000, Paul Sinnett <paul.sinnett@btinternet.com>
wrote:
FYI, I took it as an attempt to be offensive as well. Which rather surprised me, considering the civility of our conversation so far. I'm glad I merely requested further explanation of the comment. ;->I refer you to my further explanation.PS. On the subject of further explanation, what did you mean by "compilerwriters full of fear"?

The people who write compilers like Java and C# have made certain types, such
as int and long, not be objects. They cannot, for example, be nulled.

They have done this "for performance". Meanwhile, for the last twenty or more
years, the peoplw writing real OO languages have known how to make them perform
just fine without warping the model.

--
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.

Ron Jeffries
11-26-2003, 11:04 AM
On Wed, 26 Nov 2003 06:28:47 +0000, Paul Sinnett <paul.sinnett@btinternet.com>
wrote:
So I meant:1.) It's okay for you to be unconcerned with my efforts.2.) You should not consider this as an example of problems with evolvingarchitecture until you have time / interest to devote to it.

Understand. Yet my experience with this example so far gives me some fear. Time
permitting, I'll code on and see what happens. That will tell me whether the
fear is justified or not.

--
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.

Ron Jeffries
11-26-2003, 11:04 AM
On Wed, 26 Nov 2003 22:57:34 +1100, Sean Case <gsc@zip.com.au> wrote:
In article <9kd7sv02gvlh9vpgq3ucsrm0s8lh3vscnd@4ax.com>, Ron Jeffries <ronjeffries@REMOVEacm.org> wrote:[about treating missing throws as zero] Yes. But that doesn't make the program /work/ if the objective is to score frames incrementally. That makes the loop return a value, but the value isn't correct.What is the correct value in this case?

None? Blank? Don't Do This Yet?

--
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.

Ron Jeffries
11-26-2003, 11:07 AM
On Wed, 26 Nov 2003 06:24:15 +0000, Paul Sinnett <paul.sinnett@btinternet.com>
wrote:
I see. So we don't want the program to crash. And now it doesn't. But wealso want it to return the correct value for an incomplete frame. (At leastnow we know it won't crash while we're working on it.)

Actually it doesn't bother me a bit if it crashes, though I might work on
crashing first, but only because I need to come up with a way for a Frame to get
No Answer as one answer.But what is the correct value for an incomplete frame? Our refactored callwould look like this: Score( 2 );where 2 is the frame we want to score. And it returns an int value. But thatvalue will be wrong if we haven't added enough balls to the input arrayyet. So we need some way for the routine to return a value forNOT_YET_AVAILABLE.The simplest solution that comes to my mind is to define NOT_YET_AVAILABLEto be some impossible score value, such as 301 or -1. So our test would be:

Yes. I happen to have a deep fear of special values like -1 or 301, but it might
be the way to go.

I'm sure there are many ways out. None that I've thought of so far -- with very
little thought and even less programming, mind you -- look very nice to me. I
don't like trekking through the swamp when I refactor.

--
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.

Paul Sinnett
11-27-2003, 02:44 AM
Ron Jeffries wrote: The people who write compilers like Java and C# have made certain types, such as int and long, not be objects. They cannot, for example, be nulled. They have done this "for performance". Meanwhile, for the last twenty or more years, the peoplw writing real OO languages have known how to make them perform just fine without warping the model.

I thought Java had an Integer object? Doesn't C# have something like
that too?

Paul Sinnett
11-27-2003, 02:53 AM
Ron Jeffries wrote: I'm sure there are many ways out. None that I've thought of so far -- with very little thought and even less programming, mind you -- look very nice to me. I don't like trekking through the swamp when I refactor.

What does 'trekking through the swamp' mean? I provided two samples to
pass the tests. One had 3 extra 'if' statements, the other had 1. Do
either of those constitute a swamp in your opinion? If so, what is your
definition of a 'swamp'?

Ron Jeffries
11-27-2003, 03:54 AM
On Thu, 27 Nov 2003 10:44:56 +0000, Paul Sinnett <paul.sinnett@btinternet.com>
wrote:
Ron Jeffries wrote: The people who write compilers like Java and C# have made certain types, such as int and long, not be objects. They cannot, for example, be nulled. They have done this "for performance". Meanwhile, for the last twenty or more years, the peoplw writing real OO languages have known how to make them perform just fine without warping the model.I thought Java had an Integer object? Doesn't C# have something likethat too?

Well, I thought one could do it too, but, at least in C#, I can't figure out a
way to have an integer be nullable, because it is a "value type", a type
represented by its value rather than by a pointer (reference type).

C# can send messages to these things, and generally make them look like objects.
But since they are values, not references, all their bits are used up, so they
can't be nulled.

I don't know what Java does. Maybe it works differently.

--
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.

Ron Jeffries
11-27-2003, 03:58 AM
On Thu, 27 Nov 2003 10:53:42 +0000, Paul Sinnett <paul.sinnett@btinternet.com>
wrote:
Ron Jeffries wrote: I'm sure there are many ways out. None that I've thought of so far -- with very little thought and even less programming, mind you -- look very nice to me. I don't like trekking through the swamp when I refactor.What does 'trekking through the swamp' mean? I provided two samples topass the tests. One had 3 extra 'if' statements, the other had 1. Doeither of those constitute a swamp in your opinion? If so, what is yourdefinition of a 'swamp'?

I'm not criticizing your work, I'm describing how I program.

Adding three extra if statements to a program of this size feels wrong to me. I
would do it, certainly, or I'd just rip out the 29 lines and write them over.
But what I'm /trying/ to do in this context is to make the code incrementally
better, moving toward a more "object oriented" solution.

Now, since I'm not programming on this problem at the moment, but instead
programming on Extended Set Theory, I've not actually gone into the code. When I
look at the version with those if statements, it looks to me like "I wouldn't go
there."

In practice I might go there, and even if I wouldn't, no insult is implied, nor
even the suggestion that you shouldn't go their either. I'm just saying that the
code looks swampy to me and my first reaction is not to go there. It might turn
out really wonderful in a few more steps.

--
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.

Paul Sinnett
11-27-2003, 04:37 AM
Ron Jeffries wrote: On Thu, 27 Nov 2003 10:44:56 +0000, Paul Sinnett <paul.sinnett@btinternet.com>I thought Java had an Integer object? Doesn't C# have something likethat too? Well, I thought one could do it too, but, at least in C#, I can't figure out a way to have an integer be nullable, because it is a "value type", a type represented by its value rather than by a pointer (reference type).

Okay. So how about using a pointer then?:

private int * PinCount(int pinPosition) {

if ( pinPosition < rolls.Count ) {

return new int( rolls[pinPosition] );

} else {

return NULL;
}
}

Would this work?

Ron Jeffries
11-27-2003, 08:42 AM
On Thu, 27 Nov 2003 12:37:56 +0000, Paul Sinnett <paul.sinnett@btinternet.com>
wrote:
Ron Jeffries wrote: On Thu, 27 Nov 2003 10:44:56 +0000, Paul Sinnett <paul.sinnett@btinternet.com>I thought Java had an Integer object? Doesn't C# have something likethat too? Well, I thought one could do it too, but, at least in C#, I can't figure out a way to have an integer be nullable, because it is a "value type", a type represented by its value rather than by a pointer (reference type).Okay. So how about using a pointer then?:Would this work?

As it happens, I think not. pointers are "unsafe" in C#. And even if they
weren't, then we have to check the pointer at the other end, adding all kinds of
isNull. So far, I'm not seeing a sequence of changes that I like. But I'm not
working on the problem, either.

--
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.

Guest
11-28-2003, 07:45 AM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote:
The people who write compilers like Java and C# have made certain types, suchas int and long, not be objects. They cannot, for example, be nulled.

What would it mean for a number to be null? I don't see the benefit of
treating a number as an object.

For what it's worth, Java does have a way of treating the basic types as
objects. Just capitalize the first letter. Long is an object of long.
Boolean is an object of boolean. Integer is an object of int. Oops...guess
the "naming rule" doesn't quite follow through. :)

Richard MacDonald
11-28-2003, 09:01 AM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in
news:14u9sv0h5553iqp6odaehbd3qq2ui521u2@4ax.com:
The people who write compilers like Java and C# have made certain types, such as int and long, not be objects. They cannot, for example, be nulled. They have done this "for performance". Meanwhile, for the last twenty or more years, the peoplw writing real OO languages have known how to make them perform just fine without warping the model.

Sigh. Too true. So often I find myself thinking "I wish they'd stolen more
from Smalltalk :-(

Richard MacDonald
11-28-2003, 09:04 AM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in news:3fc5d41e$1
@news.totallyobjects.com:
I thought Java had an Integer object? Doesn't C# have something like that too?

The Java Integer object is useless. It can't even add. And it cannot be
null. I wind up setting its intValue to Integer.NAN when I want to model a
null. Then you try this with the Double and find the Double.NAN is a shaky
null value if you try to persist it :-(

Ron Jeffries
11-28-2003, 04:45 PM
On Fri, 28 Nov 2003 17:04:46 GMT, Richard MacDonald
<macdonaldrj@worldnet.att.net> wrote:
The Java Integer object is useless. It can't even add. And it cannot benull. I wind up setting its intValue to Integer.NAN when I want to model anull. Then you try this with the Double and find the Double.NAN is a shakynull value if you try to persist it :-(

What's Integer.NAN? Did they take away one of our perfectly good integers and
define it NAN?

--
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.

Sean Case
11-28-2003, 09:34 PM
In article <vs96ks717taedb@news.supernews.com>,
"John Roth" <newsgroups@jhrothjr.com> wrote:
None (Nill, Null, NaN, etc.) It's an error condition: the frame can't be scored yet. What the actual return should be depends on what the application needs it to be. The only thing we can say for sure is that zero isn't correct: zero is a possible score so an error return shouldn't alias it.

If it's "what the application needs" then it might well be that the
application needs zero; in the case of incremental scoring, I'd have
thought you would show the score as if all subsequent throws were zero,
with perhaps some indication that there is more to come.

Of course, if there is some ten-pin bowling equivalent of the Duckworth-
Lewis method, then you'd want to implement that instead...

Sean Case

--
Sean Case gsc@zip.com.au

Code is an illusion. Only assertions are real.

Paul Sinnett
11-29-2003, 01:29 AM
Ron Jeffries wrote: <macdonaldrj@worldnet.att.net> wrote:
The Java Integer object is useless. It can't even add. And it cannot benull. I wind up setting its intValue to Integer.NAN when I want to model anull. Then you try this with the Double and find the Double.NAN is a shakynull value if you try to persist it :-( What's Integer.NAN? Did they take away one of our perfectly good integers and define it NAN?

What if they did?

Paul Sinnett
11-29-2003, 01:43 AM
Ron Jeffries wrote: <paul.sinnett@btinternet.com> wrote:Ron Jeffries wrote:
Well, I thought one could do it too, but, at least in C#, I can't figure out a way to have an integer be nullable, because it is a "value type", a type represented by its value rather than by a pointer (reference type).
Okay. So how about using a pointer then?:Would this work?
As it happens, I think not. pointers are "unsafe" in C#. And even if they weren't, then we have to check the pointer at the other end, adding all kinds of isNull.

What do you mean by "unsafe"? Sure we'd have to do a lot of checking, but
I'm guessing that most of that checking would be the same; that is,
duplication. And we have tools for dealing with duplication.
So far, I'm not seeing a sequence of changes that I like. But I'm not working on the problem, either.

So you keep saying. Lucky for me, I don't have to please you to do TDD. All
I need to do is communicate everything I want to communicate, remove
duplication, and use the fewest classes and methods.

Paul Sinnett
11-29-2003, 02:21 AM
Ron Jeffries wrote:
I'm not criticizing your work ...

I didn't think you were.
Adding three extra if statements to a program of this size feels wrong to me. I would do it, certainly, or I'd just rip out the 29 lines and write them over.

As I pointed out earlier, we only need to add 3 statements because of the
duplication in your example. The version we refactored requires only 1
extra if statement.
But what I'm /trying/ to do in this context is to make the code incrementally better, moving toward a more "object oriented" solution.

Maybe that's the problem. Maybe the code doesn't want to go there. If we
keep throwing requirements at it, such that useful objects begin to appear,
so be it. But if we try to force our design into some pre-considered object
framework, are we still doing TDD? And if we have problems with a simple
example like this, how hard must this be on a real project?

Ron Jeffries
11-29-2003, 03:07 AM
On Sat, 29 Nov 2003 09:43:38 +0000, Paul Sinnett <paul.sinnett@btinternet.com>
wrote:
Ron Jeffries wrote: <paul.sinnett@btinternet.com> wrote:Ron Jeffries wrote:> Well, I thought one could do it too, but, at least in C#, I can't figure> out a way to have an integer be nullable, because it is a "value type",> a type represented by its value rather than by a pointer (reference> type).Okay. So how about using a pointer then?:Would this work? As it happens, I think not. pointers are "unsafe" in C#. And even if they weren't, then we have to check the pointer at the other end, adding all kinds of isNull.What do you mean by "unsafe"? Sure we'd have to do a lot of checking, butI'm guessing that most of that checking would be the same; that is,duplication. And we have tools for dealing with duplication.

In .NET, code that uses pointers directly is called unsafe and must be declared
as unsafe in the source code. Unsafe code can only run in highly trusted
environments: for example, you can't typically write the equivalent of a Java
downloadable applet in unsafe code, because everyone's security will be too high
to accept it.

Therefore, although in my opinion a pointer-based solution would be quite
natural for some apps, I'm accepting as a general constraint in C# code not to
use pointers. So far, I'm not seeing a sequence of changes that I like. But I'm not working on the problem, either.So you keep saying. Lucky for me, I don't have to please you to do TDD. AllI need to do is communicate everything I want to communicate, removeduplication, and use the fewest classes and methods.

I'm not sure of the intent just above. It sounds rather testy, though perhaps
you didn't mean it that way. I don't recall saying that you were here to please
me, and I don't believe that's your job. I'm all for you doing TDD in any way
you wish, and certainly those rules of simplicity that you quote above are rules
with which I agree.

You've been offering ideas for how to refactor my code for the bowling game, and
I've been trying to explain why I don't like them. Any of them could work --
probably would work, as you're surely a good programmer -- I'm merely saying
that they don't feel like directions I would normally want to go.

My basic point is that I don't see a sequence of changes /that I like/ for
refactoring the procedural bowling game to a solution that would support the new
requirement of displaying the frame scores incrementally as the game progresses.

I see how to /write/ the code -- my first example, all OO'd up, might be a
better place to start for example. But that's not my point: my point is just
that I don't see a sequence of changes that is as incremental as I like
refactoring to be, and I commented that I find that a bit concerning.

And my programming focus is on a different problem right now, so I'm not able to
put in the time (even if it was just a small amount) to dig around and find a
path that I like. I'll probably come back to it, just not now.

If you want to go ahead from where the code is, and show a simple set of
refactoring steps that get to the goal, that would be great. It would save me
doing it, you could have an article up on XProgramming (a great thrill I'm
sure), and it would support my beliefs better than if I did it myself. I'm just
not aiming my limited personal programming time in that direction right now.

Regards,

--
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.

Ron Jeffries
11-29-2003, 03:08 AM
On Sat, 29 Nov 2003 09:29:11 +0000, Paul Sinnett <paul.sinnett@btinternet.com>
wrote:
Ron Jeffries wrote: <macdonaldrj@worldnet.att.net> wrote:The Java Integer object is useless. It can't even add. And it cannot benull. I wind up setting its intValue to Integer.NAN when I want to model anull. Then you try this with the Double and find the Double.NAN is a shakynull value if you try to persist it :-( What's Integer.NAN? Did they take away one of our perfectly good integers and define it NAN?What if they did?

I just want to know how they did it.

--
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.

Ron Jeffries
11-29-2003, 03:14 AM
On Sat, 29 Nov 2003 10:21:25 +0000, Paul Sinnett <paul.sinnett@btinternet.com>
wrote:
Ron Jeffries wrote: I'm not criticizing your work ...I didn't think you were.

Good. Some of your recent comments led me to believe that you were ticked off or
something. Adding three extra if statements to a program of this size feels wrong to me. I would do it, certainly, or I'd just rip out the 29 lines and write them over.As I pointed out earlier, we only need to add 3 statements because of theduplication in your example. The version we refactored requires only 1extra if statement.

I must have missed something. I don't remember seeing a one-statement change
that makes the system do what we're trying to do. But what I'm /trying/ to do in this context is to make the code incrementally better, moving toward a more "object oriented" solution.Maybe that's the problem. Maybe the code doesn't want to go there. If wekeep throwing requirements at it, such that useful objects begin to appear,so be it. But if we try to force our design into some pre-considered objectframework, are we still doing TDD? And if we have problems with a simpleexample like this, how hard must this be on a real project?

Yes, that's why I'm concerned. It would be no big deal to tear up those 29 lines
and write them in some new way. But I /believe/ that smooth incremental
refactoring is [almost] always possible, and I want it to be true.

"We" are not having problems with this problem yet, neither one of us is testing
and coding on it. You're making suggestions that don't sound very good to me,
and while if we were pairing I would likely say "OK here's the keyboard, try
that for a while", neither one of us is actually testing and coding right now,
so our whoe discussion, while interesting, is a bit too speculative so far.

If and when I get back to it, I expect that I'll see a simple incremental way to
refactor in the direction I want to go, and that my beliefs will be supported.
On the other hand, I might not find such a way, and that will be interesting as
well. I might learn something new. That would be cool.

Regards,

--
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.

Ron Jeffries
11-29-2003, 03:20 AM
On Sat, 29 Nov 2003 16:34:21 +1100, Sean Case <gsc@zip.com.au> wrote:
In article <vs96ks717taedb@news.supernews.com>, "John Roth" <newsgroups@jhrothjr.com> wrote: None (Nill, Null, NaN, etc.) It's an error condition: the frame can't be scored yet. What the actual return should be depends on what the application needs it to be. The only thing we can say for sure is that zero isn't correct: zero is a possible score so an error return shouldn't alias it.If it's "what the application needs" then it might well be that theapplication needs zero; in the case of incremental scoring, I'd havethought you would show the score as if all subsequent throws were zero,with perhaps some indication that there is more to come.

Yes. One does need that indication. Scoring proceeds very incrementally, it
turns out. A frame whose time has not come at all is blank. A frame where one
ball has been thrown has at least a number (or an X) marked in it, but in
neither case is the total score "lit up". If all the balls allowed in the frame
have been thrown and the total is less than ten, then this frame and all
preceding frames can have their totals filled in. If this frame's total is ten
in two balls, then all preceding frames can be scored but this one cannot. And
so on.

So it does seem that we "need" to know not just the number of pins knocked down,
but also some other such indication. It's a bit tricky finding a "nice" way to
represent that.

Regards,

--
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.

Paul Sinnett
11-29-2003, 03:40 AM
Ron Jeffries wrote: In .NET, code that uses pointers directly is called unsafe and must be declared as unsafe in the source code. Unsafe code can only run in highly trusted environments: for example, you can't typically write the equivalent of a Java downloadable applet in unsafe code, because everyone's security will be too high to accept it. Therefore, although in my opinion a pointer-based solution would be quite natural for some apps, I'm accepting as a general constraint in C# code not to use pointers.

I see.

Paul Sinnett
11-29-2003, 03:48 AM
Ron Jeffries wrote: <paul.sinnett@btinternet.com> wrote: As I pointed out earlier, we only need to add 3 statements because of the duplication in your example. The version we refactored requires only 1 extra if statement.
I must have missed something. I don't remember seeing a one-statement change that makes the system do what we're trying to do.

from:

public int Score( int frameNumber ) {
int rollIndex = 0;
int total = 0;
for ( int frame = 0; frame < frameNumber; frame++ ) {

const bool mark = Strike( rollIndex ) || Spare( rollIndex );
const int scoringBalls = (mark)? 3 : 2;

total += PinCount( rollIndex, scoringBalls );

rollIndex += FrameSize( rollIndex );
}
return total;
}

to:

public int Score( int frameNumber ) {
int rollIndex = 0;
int total = 0;
for ( int frame = 0; frame < frameNumber; frame++ ) {

const bool mark = Strike( rollIndex ) || Spare( rollIndex );
const int scoringBalls = (mark)? 3 : 2;

if ( rollIndex + scoringBalls <= rolls.Count ) {
total += PinCount( rollIndex, scoringBalls );
}
else {
return NOT_YET_AVAILABLE;
}

rollIndex += FrameSize( rollIndex );
}
return total;
}

Paul Sinnett
11-29-2003, 04:09 AM
Ron Jeffries wrote: <paul.sinnett@btinternet.com> wrote: Ron Jeffries wrote:
So far, I'm not seeing a sequence of changes that I like. But I'm not working on the problem, either.
So you keep saying. Lucky for me, I don't have to please you to do TDD. All I need to do is communicate everything I want to communicate, remove duplication, and use the fewest classes and methods.
I'm not sure of the intent just above. It sounds rather testy, though perhaps you didn't mean it that way.

I didn't.
I don't recall saying that you were here to please me, and I don't believe that's your job. I'm all for you doing TDD in any way you wish, and certainly those rules of simplicity that you quote above are rules with which I agree.

So we can drop "sequence of changes that I like" as a constraint?
My basic point is that I don't see a sequence of changes /that I like/ for refactoring the procedural bowling game to a solution that would support the new requirement of displaying the frame scores incrementally as the game progresses.

Does it matter if you /like/ the changes as long as they get you to simple
code that works?
If you want to go ahead from where the code is, and show a simple set of refactoring steps that get to the goal, that would be great. It would save me doing it, you could have an article up on XProgramming (a great thrill I'm sure), and it would support my beliefs better than if I did it myself. I'm just not aiming my limited personal programming time in that direction right now.

As I said, I don't have C#. I could convert to C++ and go from there.

Ron Jeffries
11-29-2003, 05:56 AM
On Sat, 29 Nov 2003 12:09:31 +0000, Paul Sinnett <paul.sinnett@btinternet.com>
wrote:
I don't recall saying that you were here to please me, and I don't believe that's your job. I'm all for you doing TDD in any way you wish, and certainly those rules of simplicity that you quote above are rules with which I agree.So we can drop "sequence of changes that I like" as a constraint?

I'm not sure what you are getting at. You are making a series of suggestions for
things that I could do to my program if I were working on it. They are
interesting suggestions, but I'm not working on the program. When you make the
suggestions, I comment as to my reaction to them. Are you asking me not to
comment? My basic point is that I don't see a sequence of changes /that I like/ for refactoring the procedural bowling game to a solution that would support the new requirement of displaying the frame scores incrementally as the game progresses.Does it matter if you /like/ the changes as long as they get you to simplecode that works?

Yes, it matters to me. I have the belief that there are always small, fairly
easy and obvious steps to refactor the code from one place to another. So when I
write an article about refactoring, I like things to go that way. When they
don't, I don't like it. If you want to go ahead from where the code is, and show a simple set of refactoring steps that get to the goal, that would be great. It would save me doing it, you could have an article up on XProgramming (a great thrill I'm sure), and it would support my beliefs better than if I did it myself. I'm just not aiming my limited personal programming time in that direction right now.As I said, I don't have C#. I could convert to C++ and go from there.

Yes, you could do that if you care to. I'm sure it would be interesting and we
might learn something interesting.

I suspect that the solution one would start with, and the way one would go,
would be quite different in C++ written as C++ers write it. For example, my
concerns about pointers would be reduced or go away.

--
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.

Ron Jeffries
11-29-2003, 06:10 AM
The following is written somewhat hastily, as I have to leave in a couple of
minutes ...

On Sat, 29 Nov 2003 11:48:38 +0000, Paul Sinnett <paul.sinnett@btinternet.com>
wrote:
Ron Jeffries wrote: <paul.sinnett@btinternet.com> wrote: As I pointed out earlier, we only need to add 3 statements because of the duplication in your example. The version we refactored requires only 1 extra if statement. I must have missed something. I don't remember seeing a one-statement change that makes the system do what we're trying to do.

Um, I don't recognize the (from) version below as one that we have working,
although it looks like it could be made to work, with rather a lot more than one
statement. Here are some concerns with (from) and (to), none of them fatal.

1. I don't see what's up with calling those two variables at the beginning
const. I see that they are const inside the loop, but they aren't const inside
Score(). Perhaps I just don't understand const.

2. The definitions I know for Strike() and Spare() will throw exceptions if they
are called before there are enough rolls to feed them. I see that we could
perhaps implement them to return false if they don't have the balls (!) to
answer.

3. The PinCount() method that I remember only answers one roll's count. I see
that it could be extended to work as specified.

4. I see that the if statement and the else statement and the new undefined
thing NOT_YET_AVAILABLE (which must reflect some new notionof one statement
that I wasn't previously familiar with) are trying to protect the Score() method
from accessing balls that aren't there. But it doesn't protect Strike() and
Spare(), as mentioned above.

5. I suppose NOT_YET_AVAILABLE could be defined as -1 or some other negative
number, and I recall that you suggested something like that along the way. I
suppose the current tests would still run, and I see that we could write a test
that would not run. But that test isn't shown either.

So, unless I'm missing something, we have here an idea that's quite a bit more
than one line of code, but that could possibly work. I'd be a little irritated
by using a negative number as a flag, but when we have nothing but a raw int to
deal with that may be the best possible option.
from:public int Score( int frameNumber ) { int rollIndex = 0; int total = 0; for ( int frame = 0; frame < frameNumber; frame++ ) { const bool mark = Strike( rollIndex ) || Spare( rollIndex ); const int scoringBalls = (mark)? 3 : 2; total += PinCount( rollIndex, scoringBalls ); rollIndex += FrameSize( rollIndex ); } return total;}to:public int Score( int frameNumber ) { int rollIndex = 0; int total = 0; for ( int frame = 0; frame < frameNumber; frame++ ) { const bool mark = Strike( rollIndex ) || Spare( rollIndex ); const int scoringBalls = (mark)? 3 : 2; if ( rollIndex + scoringBalls <= rolls.Count ) { total += PinCount( rollIndex, scoringBalls ); } else { return NOT_YET_AVAILABLE; } rollIndex += FrameSize( rollIndex ); } return total;}

Regards,

--
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.

Paul Sinnett
11-29-2003, 11:22 AM
Ron Jeffries wrote: <paul.sinnett@btinternet.com> wrote:
My basic point is that I don't see a sequence of changes /that I like/ for refactoring the procedural bowling game to a solution that would support the new requirement of displaying the frame scores incrementally as the game progresses.
Does it matter if you /like/ the changes as long as they get you to simple code that works?
Yes, it matters to me. I have the belief that there are always small, fairly easy and obvious steps to refactor the code from one place to another. So when I write an article about refactoring, I like things to go that way. When they don't, I don't like it.

But what I have posted over the past few days are small, easy, and obvious
steps. It may be that you don't like any of the steps. But that is a
separate issue.
As I said, I don't have C#. I could convert to C++ and go from there.
Yes, you could do that if you care to. I'm sure it would be interesting and we might learn something interesting. I suspect that the solution one would start with, and the way one would go, would be quite different in C++ written as C++ers write it.

I see.

Paul Sinnett
11-29-2003, 06:07 PM
Ron Jeffries wrote:
1. I don't see what's up with calling those two variables at the beginning const. I see that they are const inside the loop, but they aren't const inside Score(). Perhaps I just don't understand const.

That's just a habit I got into. They are initialised at the start of each
loop, but they don't change within the loop. I use this idiom to name an
expression rather than factoring out a method.
2. The definitions I know for Strike() and Spare() will throw exceptions if they are called before there are enough rolls to feed them. I see that we could perhaps implement them to return false if they don't have the balls (!) to answer.

Indeed, and PinCount. I suggested earlier that we could prevent any of them
from throwing up by making PinCount return 0 for balls that haven't been
rolled yet. This has the side effect of making Strike and Spare both return
false as you say. We can test for these and get one step nearer a solution:

BowlingGame game;
assertEquals( 0, game.PinCount( 0, 1 ) );
assertTrue( !game.Strike( 0 ) );
assertTrue( !game.Spare( 0 ) );
3. The PinCount() method that I remember only answers one roll's count. I see that it could be extended to work as specified.

I suggested this earlier as a refactoring of your original code to remove
the duplication within the loop. I did this myself before beginning work on
the new feature.
4. I see that the if statement and the else statement and the new undefined thing NOT_YET_AVAILABLE (which must reflect some new notionof one statement that I wasn't previously familiar with) are trying to protect the Score() method from accessing balls that aren't there. But it doesn't protect Strike() and Spare(), as mentioned above.

But we need this or an equivalent before we can write a test such as:

BowlingGame game;
assertEquals( BowlingGame::NOT_YET_AVAILABLE, game.ScoreFrame( 1 ) );
5. I suppose NOT_YET_AVAILABLE could be defined as -1 or some other negative number, and I recall that you suggested something like that along the way. I suppose the current tests would still run, and I see that we could write a test that would not run. But that test isn't shown either.

-1 has worked so far.
So, unless I'm missing something, we have here an idea that's quite a bit more than one line of code, but that could possibly work. I'd be a little irritated by using a negative number as a flag, but when we have nothing but a raw int to deal with that may be the best possible option.

The only bit missing from this is the first step that I took which was to
create a new test and a new method:

BowlingGame game;
game.Roll( 0 );
game.Roll( 0 );
assertEquals( 0, game.ScoreFrame( 1 ) );

I copied int Score() to int ScoreFrame( int frameToScore ). Then in the loop
I replaced 'frame < 10' to 'frame < frameToScore'. That passed, so I
removed the duplication in Score() by replacing its contents with:

return ScoreFrame( 10 );

That worked too. Then I added another test following your original scheme:

BowlingGame game;
game.Roll( 3 );
game.Roll( 3 );
assertEquals( 6, game.ScoreFrame( 1 ) );

No problem. The rest of the test and code steps followed from there:

I tested for PinCount, Strike, and Spare on balls that hadn't been rolled.
That caused a runtime exception as expected. But I added an if to PinCount
to prevent it from counting balls that hadn't been rolled from:

int PinCount(int rollIndex, int balls) {
int pins = 0;
for ( int roll = rollIndex; roll < rollIndex + balls; roll++ ) {
pins += (int) rolls[roll];
}
return pins;
}

to:

int PinCount(int rollIndex, int balls) {
int pins = 0;
for ( int roll = rollIndex; roll < rollIndex + balls; roll++ ) {
if ( roll < (int) rolls.size() ) {
pins += (int) rolls[roll];
}
}
return pins;
}

All tests pass again. Now all I needed to do was pass the NOT_YET_AVAILABLE
test, above, which I did by adding the C++ equivalent of the statement I
posted earlier:

if ( rollIndex + scoringBalls <= (int) rolls.size() ) {
total += PinCount(rollIndex, scoringBalls);
}
else {
return NOT_YET_AVAILABLE;
}

Then a load more tests, all of which pass as is.

So what we have is a step by step evolution toward incremental frame
counting. The only step that added much was the first and that was a cut
and paste which all but disappeared in the refactoring. I suspect there is
some disguised duplication in the two checks against the size of the rolls
array that needs looking at.

There's still no frame object though. Perhaps future requirements will tease
such an object out of the code?

Ron Jeffries
11-29-2003, 07:40 PM
On Sun, 30 Nov 2003 02:07:58 +0000, Paul Sinnett <paul.sinnett@btinternet.com>
wrote:
So what we have is a step by step evolution toward incremental framecounting. The only step that added much was the first and that was a cutand paste which all but disappeared in the refactoring. I suspect there issome disguised duplication in the two checks against the size of the rollsarray that needs looking at.There's still no frame object though. Perhaps future requirements will teasesuch an object out of the code?

Interesting, and less painful than I anticipated. It's always good to forge
ahead rather than be held back by fear.

Do we kind of not like the fact that if we had frame display going on, the
Score() loop would go 1, 1 2, 1 2 3, 1 2 3 4, and so on? I suppose it would be
possible to cache results ... or just not care.

It might be that objects would come out of that. I'm not deeply concerned if
they don't, though some of the commentary elsewhere about the exercise has been,
um, complaining that the solution isn't very OO. Of course it's not very
list-oriented, or functional-programming oriented, or a lot of other -orienteds,
so I'm not inclined to worry.

Lookin' good ...

--
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.

Piers Cawley
11-29-2003, 11:02 PM
brougham5@yahoo.com writes:
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote:The people who write compilers like Java and C# have made certaintypes, such as int and long, not be objects. They cannot, forexample, be nulled. What would it mean for a number to be null? I don't see the benefit of treating a number as an object.

Your loss :)

In the case of a bowling scorer, imagine the game is represented as an
array of integers. If your integers are first class objects you can
introduce a special case integer, and use that to represent as yet
unplayed balls. Now your logic for scoring the entire frame can remain
unaltered because you set things up so that adding an
UnplayedBallScore (or whatever you call it) to the total doesn't
affect it. As your development moves on, you can subclass the
basic integer as well and move bowling score specific behaviour into
its class definition.

Piers Cawley
11-29-2003, 11:03 PM
Richard MacDonald <macdonaldrj@worldnet.att.net> writes:
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in news:14u9sv0h5553iqp6odaehbd3qq2ui521u2@4ax.com: The people who write compilers like Java and C# have made certain types, such as int and long, not be objects. They cannot, for example, be nulled. They have done this "for performance". Meanwhile, for the last twenty or more years, the peoplw writing real OO languages have known how to make them perform just fine without warping the model. Sigh. Too true. So often I find myself thinking "I wish they'd stolen more from Smalltalk :-(

The more I learn about Smalltalk, the more I find myself thinking that
about almost everything else.

--
Piers

Greg Bacon
11-30-2003, 05:51 AM
In article <7fpisvkmbkoskj46qtpk8uhfh6rl9kbnvh@4ax.com>,
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote:

: [...]
: Do we kind of not like the fact that if we had frame display going on,
: the Score() loop would go 1, 1 2, 1 2 3, 1 2 3 4, and so on? I suppose
: it would be possible to cache results ... or just not care.

YAGNI! :-)

We'll start thinking asymptotically when we have a story that tells us
we need to support games featuring arbitrary numbers of frames, so let's
just not care while the loop will run faster than an eyeblink in all
cases.

I often have to remind myself -- or at least it comes to mind *after*
I've created a nice tangle -- of Rob Pike's advice on complexity, which
is mostly YAGNI stated another way:

Rule 1. You can't tell where a program is going to
spend its time. Bottlenecks occur in surprising places,
so don't try to second guess and put in a speed hack
until you've proven that's where the bottleneck is.

Rule 2. Measure. Don't tune for speed until you've
measured, and even then don't unless one part of the
code overwhelms the rest.

Rule 3. Fancy algorithms are slow when n is small, and
n is usually small. Fancy algorithms have big
constants. Until you know that n is frequently going to
be big, don't get fancy. (Even if n does get big, use
Rule 2 first.) For example, binary trees are always
faster than splay trees for workaday problems.

[...]

See http://www.lysator.liu.se/c/pikestyle.html

: [...]

Greg
--
The glory and mystery of global commerce has been observed for thousands of
years, but it is no less wondrous to see in our everyday lives how it is
that people pursuing their self-interest in peace can only promote the
interest of society. -- Lew Rockwell

Ron Jeffries
11-30-2003, 07:43 AM
On Sun, 30 Nov 2003 13:51:12 -0000, gbacon@hiwaay.net (Greg Bacon) wrote:
In article <7fpisvkmbkoskj46qtpk8uhfh6rl9kbnvh@4ax.com>, Ron Jeffries <ronjeffries@REMOVEacm.org> wrote:: [...]: Do we kind of not like the fact that if we had frame display going on,: the Score() loop would go 1, 1 2, 1 2 3, 1 2 3 4, and so on? I suppose: it would be possible to cache results ... or just not care.YAGNI! :-)We'll start thinking asymptotically when we have a story that tells uswe need to support games featuring arbitrary numbers of frames, so let'sjust not care while the loop will run faster than an eyeblink in allcases.I often have to remind myself -- or at least it comes to mind *after*I've created a nice tangle -- of Rob Pike's advice on complexity, whichis mostly YAGNI stated another way: Rule 1. You can't tell where a program is going to spend its time. Bottlenecks occur in surprising places, so don't try to second guess and put in a speed hack until you've proven that's where the bottleneck is. Rule 2. Measure. Don't tune for speed until you've measured, and even then don't unless one part of the code overwhelms the rest. Rule 3. Fancy algorithms are slow when n is small, and n is usually small. Fancy algorithms have big constants. Until you know that n is frequently going to be big, don't get fancy. (Even if n does get big, use Rule 2 first.) For example, binary trees are always faster than splay trees for workaday problems.

I agree with the principle. And in this case it's only about six times slower
than processing each frame once, and probably so small we couldn't measure it
readily.

Still, it seems to me to be worth noticing, not because of efficiency, but
because there's something odd about recomputing the frames over and over. A
person scoring bowling wouldn't do that.

--
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.

Richard MacDonald
11-30-2003, 07:44 AM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in
news:d1rfsvs51lronbgcp4fnsd0c836dedbma5@4ax.com:
On Fri, 28 Nov 2003 17:04:46 GMT, Richard MacDonald <macdonaldrj@worldnet.att.net> wrote:The Java Integer object is useless. It can't even add. And it cannotbe null. I wind up setting its intValue to Integer.NAN when I want tomodel a null. Then you try this with the Double and find theDouble.NAN is a shaky null value if you try to persist it :-( What's Integer.NAN? Did they take away one of our perfectly good integers and define it NAN?

NAN is not-a-number. IEEE defined. Since you cannot store a null, the best
alternative would be to "standardize" on NAN as the null. But as I found
out, even that isn't so straightforward. Although I confess the problem
isn't that Java can't do it consistently, its that databases and JDBC
drivers have their own ways of treating NAN.

A stupid mess. Bloody frustrating.

Guest
11-30-2003, 09:45 AM
Piers Cawley <pdcawley@bofh.org.uk> wrote:
If your integers are first class objects you canintroduce a special case integer, and use that to represent as yetunplayed balls. Now your logic for scoring the entire frame can remainunaltered because you set things up so that adding anUnplayedBallScore (or whatever you call it) to the total doesn'taffect it. As your development moves on, you can subclass thebasic integer as well and move bowling score specific behaviour intoits class definition.

I don't understand why you wouldn't just start with the class
UnplayedBallScore. Subclassing an integer to represent business logic
doesn't sound ideal to me.

If you really want to be manipulating integers and have one be a special
case, I'm not sure what's wrong with setting up a constant to represent that
special case. Since a score can't be negative, use something like...

final static int INVALID = -1;

And refer to INVALID instead of null.

In my experience, you're better off using your own class for concepts like
ball score that keep track of whether it's played or not rather than using a
special case like INVALID or null or whatever.

Paul Sinnett
11-30-2003, 10:29 AM
Richard MacDonald wrote: Paul Sinnett <paul.sinnett@btinternet.com> wrote:
I thought Java had an Integer object? Doesn't C# have something like that too?
The Java Integer object is useless. It can't even add. And it cannot be null. I wind up setting its intValue to Integer.NAN when I want to model a null. Then you try this with the Double and find the Double.NAN is a shaky null value if you try to persist it :-(

Then for this example we could construct one. In trying to remove the
duplication of the checks against rolls.Count (rolls.size() in my version)
I created other duplication which could be removed by the creation of such
an object.

I won't list out the steps because there were a lot, although they were each
quite small. Anyway this is what popped out:

class Score
{
int value;
bool available;

Score( void ): value( 0 ), available( false ) {
}

static bool Available( const Score& lhs, const Score& rhs ) {
return lhs.available && rhs.available;
}

public:
Score( int newValue ): value( newValue ), available( true ) {
}

static Score Add( const Score& lhs, const Score& rhs ) {
if ( Available( lhs, rhs ) ) {
return Score( lhs.value + rhs.value );
}
else {
return NotYetAvailable;
}
}

static bool Equals( const Score& lhs, const Score& rhs ) {
if ( Available( lhs, rhs ) ) {
return lhs.value == rhs.value;
}
else if ( !lhs.available && !rhs.available ) {
return true;
}
else {
return false;
}
}

static const Score NotYetAvailable;
};

const Score Score::NotYetAvailable;

I also added some operator overloads - does C# have this feature?

Score operator+ ( const Score& lhs, const Score& rhs ) {
return Score::Add( lhs, rhs );
}

bool operator== ( const Score& lhs, const Score& rhs ) {
return Score::Equals( lhs, rhs );
}

This enabled me to remove the duplication in my BowlingGame class and also
got rid of -1 as a special NOT_YET_AVAILABLE value. Here a factoring of
that:

class BowlingGame
{
std::vector<int> rolls;

public:
void Roll( int roll ) {
rolls.push_back(roll);
}

Score ScoreFrame( int frameToScore ) {
int rollIndex = 0;
Score total = 0;
for (int frame = 0; frame < frameToScore; frame++) {
const int scoringRolls = MarkFrame(rollIndex) ? 3 : 2;
total = total + PinCount(rollIndex, scoringRolls);
rollIndex += FrameSize(rollIndex);
}
return total;
}

Score ScoreGame( void ) {
return ScoreFrame(10);
}

int FrameSize(int rollIndex) {
if (Strike(rollIndex)) return 1;
return 2;
}

Score PinCount(int rollIndex) {
if ( rollIndex < (int) rolls.size() ) {
return Score( (int) rolls[rollIndex] );
}
else {
return Score::NotYetAvailable;
}
}

Score PinCount(int rollIndex, int balls) {
Score pins = 0;
for ( int roll = rollIndex; roll < rollIndex+balls; roll++ ) {
pins = pins + PinCount(roll);
}
return pins;
}

bool Strike(int rollIndex) {
return PinCount(rollIndex) == Score(10);
}

bool MarkFrame(int rollIndex) {
return Strike(rollIndex) || PinCount(rollIndex,2) == Score(10);
}
};

Jeff Grigg
11-30-2003, 06:31 PM
>> Richard MacDonald <macdonaldrj@worldnet.att.net> wrote:The Java Integer object is useless. It can't even add.

Right. Very right. 'java.lang.Integer' is pretty lame.
And it cannot be null.

??? True, an Integer instance can't be null. But a Java Integer
variable can contain the value null instead of containing a pointer to
a valid Integer instance.
I wind up setting its intValue to Integer.NAN when I want tomodel a null. Then you try this with the Double and find theDouble.NAN is a shaky null value if you try to persist it :-(
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote: What's Integer.NAN? Did they take away one of our perfectly good integers and define it NAN?

Richard MacDonald <macdonaldrj@worldnet.att.net> wrote... NAN is not-a-number. IEEE defined. Since you cannot store a null, the best alternative would be to "standardize" on NAN as the null. But as I found out, even that isn't so straightforward. Although I confess the problem isn't that Java can't do it consistently, its that databases and JDBC drivers have their own ways of treating NAN. A stupid mess. Bloody frustrating.

There is no such thing as 'Integer.NAN'.
See:
http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Integer.html

"Infinity" and "Not A Number" are properties (valid values) of
floating point numbers -- Double and Float:
http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Double.html
http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Float.html


You're right that variables of type 'double' can't be null. But
variables of type 'Double' can. I've had plenty of experience
recently (mostly unpleasant ;-) with null Double and BigDecimal
values. Believe me, they can be null. (And if you're not careful,
they're likely to be, at the least opportune moment. ;-)


And with respect to databases... Double check the data types used on
your tables. Oracle schemas typically use NUMBER, which is a packed
decimal representation, not a "scientific" floating point notation.
So it can't store +inf, -inf or NaN. (On the other hand, you might
try using NULL. ;-)

Chris Dollin
12-01-2003, 02:12 AM
brougham5@yahoo.com wrote:
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote:The people who write compilers like Java and C# have made certain types,such as int and long, not be objects. They cannot, for example, be nulled. What would it mean for a number to be null? I don't see the benefit of treating a number as an object.

If a number is null, it isn't there.

[In a language design I did, the name for the "no value here, guv"
value is "absent", not "null". Better connotations, I think.]

The benefit of realising that numbers *are* objects is uniformity.

--
Chris "electric hedgehog" Dollin
C FAQs at: http://www.faqs.org/faqs/by-newsgroup/comp/comp.lang.c.html
C welcome: http://www.angelfire.com/ms3/bchambless0/welcome_to_clc.html

Piers Cawley
12-01-2003, 03:08 AM
brougham5@yahoo.com writes:
Piers Cawley <pdcawley@bofh.org.uk> wrote:If your integers are first class objects you can introduce a specialcase integer, and use that to represent as yet unplayed balls. Nowyour logic for scoring the entire frame can remain unaltered becauseyou set things up so that adding an UnplayedBallScore (or whateveryou call it) to the total doesn't affect it. As your developmentmoves on, you can subclass the basic integer as well and movebowling score specific behaviour into its class definition. I don't understand why you wouldn't just start with the class UnplayedBallScore.

Because, in the case we're discussing, Ron is trying to refactor a
procedural implementation into something which can deal with partially
played games.
Subclassing an integer to represent business logic doesn't sound ideal to me.

Well, no, but if you don't want to go changing logic everywhere
whenever you change anything, you need something that fits into your
initial object as if it were an integer. In a language which isn't as
anal about types as Java is you wouldn't have to subclass your
integer, you could just implement something that responds to the
messages your debugger tells you it needs to respond to.

If you really want to be manipulating integers and have one be a special case, I'm not sure what's wrong with setting up a constant to represent that special case. Since a score can't be negative, use something like... final static int INVALID = -1; And refer to INVALID instead of null.

But then you have to have logic everywhere to deal with the INVALID
case, which is what Ron specifically wanted to avoid. By introducing a
special case object, the only change you have to make is in how you
populate the array of scores, all the other logic stays the same,
which is no bad thing when you're growing the design in small steps.
In my experience, you're better off using your own class for concepts like ball score that keep track of whether it's played or not rather than using a special case like INVALID or null or whatever.

Indeed. But the existing solution is a simple procedural
one. Introducing a special case object that quacks like an integer
from the point of view of the scoring loop is a useful transitional
step if your language allows it.

Duncan Booth
12-01-2003, 08:37 AM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in
news:14u9sv0h5553iqp6odaehbd3qq2ui521u2@4ax.com:
The people who write compilers like Java and C# have made certain types, such as int and long, not be objects. They cannot, for example, be nulled. They have done this "for performance". Meanwhile, for the last twenty or more years, the peoplw writing real OO languages have known how to make them perform just fine without warping the model.

Microsoft did actually include some nullable value types in C# (or at least
the CLR). In their infinite wisdom though they realised that the only
reason for making an integer nullable was so you could store it in a
database :^) See System.Data.SqlTypes.SqlInt32 (and similar for other
nullable value types). They interact fairly cleanly with the ordinary
value types, casts from Int32 to SqlInt32 are implicit, but you need an
explicit cast to go the other way.

Of course, since its a value type you can't actually set one to null, you
have to use the SqlInt32.Null value instead. Also, for reasons that totally
escape me, arithmetic operations involving a Null value return Null instead
of throwing an exception. e.g.

SqlInt32 x = 42;
Console.WriteLine(x+SqlInt32.Null);

outputs Null.


--
Duncan Booth duncan@rcp.co.uk
int month(char *p){return(124864/((p[0]+p[1]-p[2]&0x1f)+1)%12)["\5\x8\3"
"\6\7\xb\1\x9\xa\2\0\4"];} // Who said my code was obscure?

Richard MacDonald
12-03-2003, 07:49 PM
jgrigg@mo.net (Jeff Grigg) wrote in news:c794c0fd.0311301831.6422627
@posting.google.com:

Your corrections remind me once again never to vent from memory...truth
usually suffers in the interests of the story :-)


MyLounge.com Site Map
Forum: Cars, Cell Phone, Database, Games, Home Improvement, IT, Music, School, Sports, Web Design, Web Server, Weight Loss

The MyLounge.com forum is intended for informational use only and should not be relied upon and is not a substitute for any advice. The information contained on MyLounge.com are opinions and suggestions of members and is not a representation of the opinions of MyLounge.com. MyLounge.com does not warrant or vouch for the accuracy, completeness or usefulness of any postings or the qualifications of any person responding. Please consult a expert or seek the services of an attorney in your area for more accuracy on your specific situation. Please note that our forums also serve as mirrors to Usenet newsgroups. Many posts you see on our forums are made by newsgroup users who may not be members of MyLounge.com Term of Service