Roguelike Iterative Test-Driven Development in Java, Part 3

From RogueBasin
Revision as of 01:52, 11 August 2012 by Qbradq (talk | contribs) (→‎Design)
Jump to navigation Jump to search

In this part we finally get to write some code! This part corresponds to HWR15 Part 3. In a traditional waterfall development pattern this is the easiest part to implement because it's the most basic. It's also the time during which you have the most opportunity to make a design mistake that will cause you grief later on.

Requirements

  1. Screen Output
  2. Keyboard Input
  3. Main Menu
  4. Game Mode
    1. Message Display
    2. Test Character Display
    3. Movement Keys

Personally I like to start with a main menu. It seems like when I design a game starting with the game mode it's harder to add a menu later.

Design

If you've never done Test-Driven Development before this is going to sound rather lame. In order to make the most simple portions of our program testable we'll need to over-engineer them. Specifically we need to abstract the basic services (screen output and keyboard input) behind interfaces that abstract the implementation of the service from the consumption of that service.

Luckily for us libjcsi takes care of the screen output abstraction. It does not provide a test implementation but we don't really need one. Screen output is our view layer, like the HTML rendering portion of a web application or the presentation layer of a thin-client application. View layers typically have many aesthetic requirements and few, if any functional requirements. We don't unit test aesthetic requirements; that's what acceptance testing is for. And our particular view layer will have no functional requirements, so it will not be unit tested.

Some concrete implementation is required for the other portions of the system, and I'd rather not have console windows popping open and going crazy while the unit tests run. For that reason we'll develop a stub implementation of libjcsi's output interface. Stub implementations are just what they say they are: a concrete implementation of an interface in which most or all methods are stubs. This effectively makes every operation a no-op.

libjcsi also abstracts keyboard input for us. The problem is there's no way to provide mock input. We need an additional layer of abstraction if we want to support key mappings and macros in the future, so instead of creating a test implementation for keyboard input we'll go ahead and create the input abstraction interface, a test implementation and an intermediate implementation that translates key presses into input actions.

Menu and game modes are not service providers, rather they are service consumers. We won't need multiple levels of abstraction to test them, however a single layer of abstraction (an interface) allows easier expansion and more flexible design. And a base implementation (base class) helps code reuse.

So here's what our class diagram looks like:

  • net.slashie.libjcsi.ConsoleSystemInterface - Console system interface provided by libjcsi
    • com.qbradq.durok.lib.ConsoleSystem - Base class wrapping our screen output and keyboard input services
      • com.qbradq.durok.impl.test.TestConsoleSystem - Stub implementation of console services for testing
      • com.qbradq.durok.impl.JCursesConsoleSystem - Implementation based on JCurses
      • com.qbradq.durok.impl.WSwingConsoleSystem - Implementation based on WSwing
      • com.qbradq.durok.impl.ConsoleSystemFactory - Factory provider for ConsoleSystem implementations
  • com.qbradq.durok.lib.InputProviderInterface - Interface for the input service provider
    • com.qbradq.durok.impl.test.TestInputProvider - Implementation specializing in providing pre-defined test input streams
    • com.qbradq.durok.impl.InteractiveInputProvider - Implementation that converts interactive user input into input streams
  • com.qbradq.durok.game.GameModeInterface - Interface for anything that enters an input consumption loop
    • com.qbradq.durok.game.GameModeBase - Base implementation of a game mode providing sane default behavior
      • com.qbradq.durok.game.MainMenuMode - The main menu
      • com.qbradq.durok.game.GameMode - The main game interface

Tests