Observer pattern
From GPWiki
The wiki is now hosted by GameDev.NET at wiki.gamedev.net. All gpwiki.org content has been moved to the new server. However, the GPWiki forums are still active! Come say hello. The Observer shows how to separate problem areas and create a loose event driven object relationship.
[edit] Definition[edit] ProblemImagine that you are making a first person shooter 3D action game like Half-Life. You have a main character that you are controlling, and you have a console that you can type commands in. The game should feature multiple people online gameplay using a dedicated server. To maximize the capacity of the server, it should not display graphics on screen but use all resources to handle game logic and networking. It should, however, keep a log of ingame actions to create statistics and check for cheaters. The game will need to have the following features: Multiple input modes (moving your character and writing commands), ability to disable graphical routines, an action logging system, easy implementation of a multiplayer mode. How would you go about making a flexible and stable architecture for that system? The Observer pattern and the Model-View-Controller architecture, an extension of the Observer pattern, teaches how to create such a dynamic architecture. [edit] Context
[edit] Solution
The Subject holds the raw data, and the Observer displays the data.
The Model holds the raw data and logic, the Controller invokes methods of the Model, and the View displays the data in some way.
[edit] Example Usage - Event BasedLet's try applying the Model-View-Controller architecture to a FPS game system like the one described above, but highly simplified.
Pseudo code: interface Input { void handleInput(); } class TexturalInput implements Input { private GameEngine ge; public TexturalInput( GameEngine ge ) { this.ge = ge; } public void handleInput() { // If we've got an input ge.modifyWorldData( "Text input" ); } } // For the sake of simplicity, I'll like the graphical classes out but // the functionality is basically the same as its textural counterpart class GraphicalInput implements Input { private GameEngine ge; public GraphicalInput( GameEngine ge ) { /* Code */ } public void handleInput() { /* Code */ } } class GameEngine { private List<Display> displays; private String worldData; public GameEngine() { displays = new List<Display>(); worldData = "Nothing"; } public void attach( Display d ) { d.setGameEngine( this ); displays.add( d ); } public void updateAll() { for ( Display d : displays ) { d.update(); } } public void modifyWorldData( String newData ) { worldData = newData; updateAll(); } public String getWorldData() { return "WORLD DATA: " + worldData; } } interface Display { void setGameEngine( GameEngine ge ); void update(); void draw(); } class TexturalDisplay { private GameEngine ge; public void setGameEngine( GameEngine ge ) { this.ge = ge; } public void update() { draw(); } public void draw() { print( ge.getWorldData() ); } } class GraphicalDisplay { private GameEngine ge; public void setGameEngine( GameEngine ge ) { /* Code */ } public void update() { /* Code */ } public void draw() { /* Code */ } } Test: GameEngine ge = new GameEngine(); ge.attach( new TexturalDisplay ); TexturalInput ti = new TexturalInput( ge ); ti.handleInput(); Output: WORLD DATA: Text input Here I call the handleInput() method of TexturalInput manually for the sake of the example. Normally you would hook the input classes up with the controller handling routines. Let's go back and see how our system could provide a framework for all the required features of the game. The game system need;
[edit] Example Usage - Passive ControllerThe Observer pattern or the Model-View-Controller architecture isn't limited to large frameworks of classes such as a game system, but can also be used on single classes. In this example we'll try to model a military tank using the MVC architecture. We'll use a somewhat different approach compared to the last example, because we'll let our model class read the data in our controller class instead of having the controller class invoke methods on the model.
Pseudo code: class TankController { public float getSpeed() { /* Code */ } public float getTurretAngle() { /* Code */ } } class Tank { private List<View> views; private TankController control; public Tank() { views = new ArrayList<View>(); control = new TankController(); } public void attach( View v ) { views.add( v ); } public String getTankData() { return "Tank data here"; } public void run() { newTankSpeed = currentTankSpeed + control.getSpeed(); newTurretAngle = currentTurretAngle + control.getTurretAngle(); updateAll(); } public void updateAll() { for ( View v : views ) { v.update(); } } } interface View { void update(); void draw(); } class TankView { private Tank tank; public TankView( Tank t ) { tank = t; } public void update() { draw(); } public void draw() { String tankData = t.getTankData(); print( "Data: " + tankData ); // draw tank } } Test: Tank t = new Tank(); t.attach( new TankView( t ) ); t.run(); Output: Data: Tank data here Using the Observer pattern and the Model-View-Architecture require typing in some more code than you'd otherwise have to, but it pays off tenfold later in development. It'll provide better stability, extendability and maintainability. It's one of the most widely recognized computer science design patterns around, so use it and save youreself a lot of trouble.
[edit] Additional Information |








