Something I said

Pattern bloat in Java, an example with poker

by edA-qa mort-ora-y on Wednesday July 23 2008 @ 17:21:24 (13/-1293 Points)

Computing ↪Technology ⌦ Reply ✔ Stick It ✗ Ditch It

Some time ago the wealth of patterns was gifted upon the world of programming. At first they were beautiful, giving name and purpose to a once chaotic swirl of functions and data. Once embraced however, they swelled and engulfed all which we knew. Still now, not even the tiniest of programs manages to escape the bloating behemoth of the pattern.

It's sad really. And it is Java's fault.

Example

I was recently writing some code to detect the best poker hand in a set of cards. It is the type of problem that can be easily broken down into several steps. Here one can simply test for each poker hand type in turn, starting from the highest, and return as soon as one is found.

This means that at some point you end up with a series of functions something like below.

function bestStraightFlushOf( cards )
function bestFourOfAKindOf( cards )
function bestFullHouseOf( cards )
function bestFlushOf( cards )
function bestStraightOf( cards )
function bestThreeOfAKindOf( cards )
function bestTwoPairOf( cards )
function bestPairOf( cards )

All you need to do now is take your cards, pass to each function in turn, and then return the highest valued hand. Since the order above is the correct ranking order, from highest to lowest, we simply want to call the functions in that order, returning as soon as a hand is found.

The first two steps look like this:

hand = bestStraightFlushOf( cards );
if( hand != null )
  return hand;
  
hand = bestFourOfAKindOf( cards );
if( hand != null )
  return hand;

The C Way

Obviously this starts to repeat itself rather fast, introducing an unwanted redundancy in the code. Were I using C, or C++, I would simply create an array of function pointers (excuse my syntax, I haven't actually done this in C in a while):

BestFunc funcs[] = { 
  bestStraightFlushOf,
  bestFourOfAKindOf,
  bestFullHouseOf,
  ...,
  0
};

for( BestFunc f = funcs; f != 0; f++ ) {
  hand = (*func)( cards );
  if( hand )
    return hand;
}

I was however using haXe, and its concept of function pointers is close enough to the same that the above pattern still works. So I use it.

The Java Way

Though what if you were using Java, which doesn't have function pointers of any kind?

If you were a progressive thinker and allowed yourself to use a macro preprocessor on top of Java you'd have many options, and would likely end up with something looking like this list:

CheckHand(StraightFlush,cards)
CheckHand(FourOfAKind,cards)
...

Though since Java doesn't play well with preprocessors (with respect to line numbers), this may not be a great option. So a suggestion came to me to use the command pattern. For reference, that means that every hand test would become its own class, looking something like this:

package poker.hand;

import poker.*;
import java.util.*;
...

/**
 * A command class to test blah blah blah
 */
class TestFlush() implements TestHand {
  @Override
  public Hand bestOf( Stack stack ) {
    ...
  }
}

And then you can have an array of all those command objects and do the same loop iteration as in C.

This is just silly, wasteful bloat. Not only do you have many lines of overhead for each function, you now also have an additional ten source files to maintain (nine hands, one interface). There is of course also a performance overhead, but in this situation it is not likely significant.

I'm guessing this is also just the start of the swelling. Just imagine the possibilities of using inheritance to define every hand type, specialized comparators, factory classes to produce decks and stacks, and even some serializing classes to transmit them via SOAP! Granted Java as a language didn't dictate you had to do this, but with a lack of function pointers and no macros, it certainly doesn't make it easy to avoid it.

Oh, the haXe programmer or whatever it is has finished talking. I guess I should say something now. :) My main comment is that you don't have to put every Java class in its own file. That's only required, as far as I know, for public classes.

File CardStack.java: ==================== public class CardStack { private Stack cards;

...

public CardStack findBestHand() { BestHandCommand[] commands = new BestHandCommand[] { new BestStraightFlushCommand(), new BestFourOfAKindCommand(), ... };

for (BestHandCommand command : commands) { CardStack hand = command.findBestHand(); if (hand != null) return hand; } } }

class BestStraightFlushCommand { ... }

etc...

by Vlad on Thursday July 24 2008 @ 23:56:08

My last comment didn't get formatted and had a few small errors (the command classes would probably be inner classes of CardStack for example). Anyway, I should mention that Java does have fairly powerful reflection capabilities, so you could also use reflection to store an array of functions and invoke them in a loop. The syntax last time I checked was admittedly rather ugly. It might be nicer nowadays... Not sure.

by Vlad on Friday July 25 2008 @ 00:31:30

Using reflection to simulate function pointers is truly awful in Java. There is not short form, you have to load the class, collect the signatures, pack parameters into an array, etc... Plus it isn't performant (significant overhead last time I checked).

by edA-qa mort-ora-y on Saturday August 02 2008 @ 18:39:32

© 2008 edA-qa mort-ora-y
Using Persephone and TestPlan