Follow the Messages
We can benefit from this high-level, declarative approach only if our objects are designed to be easily pluggable. In practice, this means that they follow common communication patterns and that the dependencies between them are made explicit. A communication pattern is a set of rules that govern how a group of objects talk to each other: the roles they play, what messages they can send and when, and so on. In languages like Java, we identify object roles with (abstract) interfaces, rather than (concrete) classes—although interfaces don't define everything we need to say.
In our view, the domain model is in these communication patterns, because they are what gives meaning to the universe of possible relationships between the objects. Thinking of a system in terms of its dynamic, communication structure is a significant mental shift from the static classification that most of us learn when being introduced to objects. The domain model isn't even obviously visible because the communication patterns are not explicitly represented in the programming languages we get to work with. We hope to show, in this book, how tests and mock objects help us see the communication between our objects more clearly.
Here's a small example of how focusing on the communication between objects guides design.
In a video game, the objects in play might include: actors, such as the player and the enemies; scenery, which the player flies over; obstacles, which the player can crash into; and effects, such as explosions and smoke. There are also scripts spawning objects behind the scenes as the game progresses.
This is a good classification of the game objects from the players' point of view because it supports the decisions they need to make when playing the game—when interacting with the game from outside. This is not, however, a useful classification for the implementers of the game. The game engine has to display objects that are visible, tell objects that are animated about the passing of time, detect collisions between objects that are physical, and delegate decisions about what to do when physical objects collide to collision resolvers.
As you can see in Figure 2.2, the two views, one from the game engine and one from the implementation of the in-play objects, are not the same. An Obstacle, for example, is Visible and Physical, while a Script is a Collision Resolver and Animated but not Visible. The objects in the game play different roles depending on what the engine needs from them at the time. This mismatch between static classification and dynamic communication means that we're unlikely to come up with a tidy class hierarchy for the game objects that will also suit the needs of the engine.
Figure 2.2 Roles and objects in a video game
At best, a class hierarchy represents one dimension of an application, providing a mechanism for sharing implementation details between objects; for example, we might have a base class to implement the common features of frame-based animation. At worst, we've seen too many codebases (including our own) that suffer complexity and duplication from using one mechanism to represent multiple concepts.