Saturday, February 6, 2016

I've Got a Factory in My Factory in My Class Hierarchy of Factories (or Something Like That)


Yesterday I worked up a Dart example of the factory method pattern in which a "game factory" could pit two players against each other in a series of board games:
main() {
  var series = new GameFactory('Professor Falken', 'Joshua');

  series
    ..start()
    ..createBoardGame('Checkers').play()
    ..createBoardGame('Thermo Nuclear War').play()
    ..createBoardGame().play();
}
Most of the concrete implementation of the pattern is placeholder print() statements. The code so far establishes a series of games between two players, creates a board game via the factory method from which the pattern gets its name, then plays the game. Currently, this results in output along the lines of:
$ ./bin/board_game.dart
*** Professor Falken vs. Joshua ***
  CheckersGame
    Player One starts with: 12 pieces
    Player Two starts with: 12 pieces
    --
    Winner: Player One

  ThermoNuclearWar
    Player One starts with: 1,000 warheads
    Player Two starts with: 1,000 warheads
    --
    Winner: None

  ChessGame
    Player One starts with: 1 king, 1 queen, 2 rooks, 2 bishops, 2 knights, 8 pawns
    Player Two starts with: 1 king, 1 queen, 2 rooks, 2 bishops, 2 knights, 8 pawns
    --
    Winner: Player One
That seems a decent example of the pattern except that the placeholder code is starting out with ugliness. Specifically, the products in the patterns—the various board games—are on the repetitive side:
class ChessGame extends BoardGame {
  List playerOnePieces = [
    '1 king', '1 queen', '2 rooks', '2 bishops', '2 knights', '8 pawns'
  ];
  List playerTwoPieces = [
    '1 king', '1 queen', '2 rooks', '2 bishops', '2 knights', '8 pawns'
  ];
}
What the various BoardGame classes need is a common way to create the initial set of pieces on the board. If only there were a design pattern that could help with that…

Say, that sounds like a factory method pattern! Now, I know what you're thinking, a factory inside a factory—that's typical pattern fanboyism. I'm not going to argue with you, but I beg your patience for just a bit...

If BoardGame is now a factory, I need it to declare a factory method for its subclasses to implement. Since it needs to generate game pieces for the start of a game, I have it require a _createPieces() method:
abstract class BoardGame {
  GamePieces playerOnePieces, playerTwoPieces;
  BoardGame() {
    playerOnePieces = _createPieces();
    playerTwoPieces = _createPieces();
  }
  GamePieces _createPieces();
  // ...
}
Both player one and player two will be assigned the same number of pieces to begin, so the constructor assigns both player one's and player two's pieces to the results of the _createPieces factory method. But the abstract BoardGame does not know how to create chess, checkers, etc. pieces. Subclasses need to define these:
class ChessGame extends BoardGame {
  GamePieces _createPieces() => new ChessPieces();
}

class CheckersGame extends BoardGame {
  GamePieces _createPieces() => new CheckersPieces();
}

class ThermoNuclearWar extends BoardGame {
  GamePieces _createPieces() => new ThermoNuclearPieces();
  String get winner => "None";
}
With that, my board game classes are significantly DRYer and better able to define the functionality behind the actual game play.

But back to the factory-in-a-factory craziness that I have inflicted on myself. I think it actually makes sense. From an illustrative standpoint, it is over the top, but it feels like a reasonable, organic structure. Perhaps last night's GameFactoryGameBoard creator/product was somewhat artificial, but I need to generate different gameboards at runtime, so that makes a decent amount of sense. If anything, tonight's approach feels even better. Each boardgame needs to layout and assign pieces to the players, so a factory method serves nicely in that capacity.

In fact, it makes so much sense that it is a flavor of the factory method pattern: the parallel class hierarchy variation. By taking this approach, I know that, for each type of board game in the BoardGame class hierarchy, there will be a corresponding entry in the hierarchy of the GamePieces classes. The ChessGame needs to use ChessPieces in order to populate the game:
class ChessGame extends BoardGame {
  GamePieces _createPieces() => new ChessPieces();
}
Armed with that knowledge, the client code can make use of this as well. For example it could restart a game or start a new one:
  series.start();
  var game;
  game = series.createBoardGame('Checkers');
  game.play();
  // Start over
  game.playerOnePieces = game._createPieces();
  game.playerTwoPieces = game._createPieces();
  game.play();
That might be a little more useful in another example (a restart() method would make most sense in this example), but it still serves to illustrate some of the potential of this parallel hierarchy approach. I may examine a different example tomorrow to get a better handle on this. Or I may switch to mirrors. Either way, it is sure to be more fun with factories!

Play with the code on DartPad: https://dartpad.dartlang.org/639e49cfc1b2ee3cb82a.


Day #87

No comments:

Post a Comment