Showing posts with label MVP. Show all posts
Showing posts with label MVP. Show all posts

Saturday, February 21, 2009

Alright, so I blew through the first two stores that I was attempting:

1. User opens the application and sees the game board. Game board is a chess board (8 x 8 - alternating black and white squares) background is a gray. Camera is looking at the center from above and toward one side.

2. The user has one piece (a blue ball) on the board that is located on one side of the board on one of the center squares.

And up until now there was really no design to it. I started coding a class that had the capability of drawing on the 3D canvas and just kept going. Neither of these stories contain any user interaction yet so I was having a hard time coming up with testable code.

I eventually saw that there was a bit of logic needed for creating the checkered game board. So I thought I'd extract out something that would need to know how to do that. So I started going into an MVP pattern. I wanted the view to get something that it could use to create the proper rows and columns with the right alternating colors without too much logic. It wound up looking like this:

public void constructGrid(GameGridData data) {
for (int x = 0; x < data.getTileData().length; x++) {
for (int z = 0; z < data.getTileData()[0].length; z++) {
Tile tile = new Tile(data.getTileData()[x][z]);
board.addChild(tile);
}
}
}

The Tile class was an abstraction I had done to encapsulate the creation of the geometry and details of creating the individual squares. The TileData is a bean that I had to construct to keep the back-end models from knowing anything about the Java3D APIs. The GameGridData has the algorithm needed to get all the positions and colors in the right data structure (the TileData bean) that the view needed. I have a feeling that GameGridData may morph into an abstract class where subclasses will be specific for Chess boards or terrain looking grids or vast spans of black space. Once that was all pulled apart, I was able to construct a Model that generated these TileData beans and a Presenter that could communicate between the two.

Now what I have is a bunch of smaller classes that don't contain "view code" all of which are very testable! So that's where the first real signs of a design started to form. I had a need to be able to test what I was doing and no real good way of isolating the code that needed testing. So by separating out what was just calls to the framework's API (or my abstractions around the framework) and the logic needed for the correct calls, I was able to write a few tests and get things a bit more agile.

The third story actually got me into some user interaction:

3. The user can select a square on the board and the ball will move to that square. Movement is shown and not just a sudden change in location.

Selecting an object in Java3D is a bit more complicated than in Swing. Picking an object is basically translating a point that your mouse picked on the screen to a ray or cone that extends from the point down into the canvas and then seeing what objects intersect with that ray or cone. So my abstractions around the actual tiles in the grid by my Tile class paid off when I found out that the API will return the Node or Shape3D object that was in the intersection path. So I was able to retrieve the same TileData bean that I used to create the selected Tile object and then notify the presenters that are listening to the view.

And this is what it wound up looking like:

final PickCanvas pickCanvas = new PickCanvas(canvas3D, board);
pickCanvas.setMode(PickInfo.PICK_GEOMETRY);
pickCanvas.setTolerance(4.0f);

canvas3D.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent mouseEvent) {
pickCanvas.setShapeLocation(mouseEvent);
PickInfo pickClosest = pickCanvas.pickClosest();
if (pickClosest != null) {
Tile tile = (Tile) pickClosest.getNode();
selectedTile = tile.getTileData();
tileSelectedListeners.notifyListeners();
}
}
});


So then I needed a way to move the user's game piece once the position selection took place. I made some similar refactorings to the code that created the user's piece with a MVP pattern. And then I let the model from the game grid and the model from the user piece be able to communicate with each other. And then the piece model notified it's presenter which in turn told the view to move the piece to the correct location.

public UserPieceModel(final IGameGridModel gameGridModel) {
gameGridModel.addPositionSelectedListener(new IListener() {
public void fireEvent() {
currentPosition = gameGridModel.getSelectedPosition();
adjustCurrentPositionForHeight();
modelListenerManager.notifyListeners();
}
});
}


So this story is just about wrapped up. I currently just have the user's piece suddenly jumping to the new location but the story has some more specific requirements: "Movement is shown and not just a sudden change in location" I made it that way intentionally because I know nothing of Java3D's animation APIs. So now I'm just doing a quick spike to determine how to do that and then I'll be able to finish this story up and move on to the next.

So overall I think things are progressing nicely. I wasn't liking where this was going at first with a whole bunch of un-testable UI code but now it seems like I've got the start of a design that allows me to test what I'm creating. And really that's the point of this blog. I called it Emergent Development because that's what good software development should be. You start going and you realize you need something so that you can make it more testable, more loosely coupled, more flexible and so you interject a pattern or two so you can test your stuff and just keep going. So your design comes from need not from a over thought-out UML diagram that was created long before any real code started. Design comes as you need it, no sooner.

Saturday, February 14, 2009

Doing a GUI application using TDD

Testing user interfaces is always a pain point when doing TDD GUI apps. Web development has been able to get around this a bit with automated UAT testing using tools like Selenium or Watir. But with GUI apps, there's no really good, cheap (as in free) UAT tools (if anyone knows of one, please let me know!!!).

So when I create a GUI app I use a MVP pattern where the View is really, really thin because it's just impossible to right good tests around it. In the past with Swing and SWT/RCP application, I've been able to have the view be fairly simple with an exposed getter/setter-ish API on it so that the Presenter can be tested on how it manipulates the View and the Model can have it's own tests for the business rules and that leaves not a whole bunch to test in the View. You don't really need tests for the implementat of view.getUsername() when all it does is read the text from a text field.

Here's a sample login MVP of what I'm talking about...
(I didn't compile this so don't complain if it doesn't actually work)

public class Presenter {
public MyPresenter(final IView view, final IModel model) {
view.addLoginListener(new IListener() {
public void fireEvent() {
try {
model.authenticate(view.getUsername(),
view.getPassword());
} catch (AuthenticationException e) {
view.showErrorMessage(e.getMessage());
}
}
});
}
}


The API exposed on the view is real simple:

public interface IView {
void addLoginListener(IListener listener);
String getUsername();
String getPassword();
void showErrorMessage(String errorMessage);
}


And the API on the model is real simple too:

public interface IModel {
String authenticate(String username, String password)
throws AuthenticationException;
}


So this example makes the presenter very testable. And it separates out the need to know how the stuff is displayed to the user verses and how the actual authentication needs to happen. And the model is is left with a single responsibility and a very simple API. So testing the model is now real easy also. And the view now has no complicated functionality.

Even if you wanted to have some fields in the view that enable or disable based on options that the user has selected in other parts of the view, you can create another MVP triad for that and you could have another interface for that the view implements that might have some methods that look like this...

void enableSubmitButton();
void disableSubmitButton();

So the View doesn't even know what the state of the submit button is. It just knows how to enable or disable the field. Again, all the logic can go off to a Model so that it is testable and the Presenter just acts as a mediator between the View and Model interfaces which keeps the Presenter really testable.

As a general rule of thumb, you write a test for anything that could possibly break. And at the point of where this view is at, what would you be testing? That Swing is working? You shouldn't be testing their code, just yours.

Now in my game, I'm using Java3D for my "View" so that's going to be a fairly complicated piece that needs to be split as thin as possible to provide testability. Because really that should be your goal: how can I make this code testable? If I abstract something out and hide it behind an interface and inject it in, I can test my classes better.