简体   繁体   中英

“static class” or pass references around?

I'm part of a team designing the server for a client/server model naval warfare video game (University course). We have a fairly concrete (well, I think) system design, but there is one aspect that bothers me.

The basic layout of the system is:

Server [thread] (handles incoming connections)
|
Game [thread] (deals with game events in it's work queue and sending messages to clients)
|--Environment (holds environment variables, deals with collision)
|--Client(s) [thread] (handles incoming messages from client sockets and adds events to the Game's work queue)
    |--Ship (holds game data, eg: hit points, fire power, etc)
    |--Parser (parses client messages and creates game event objects)
|--GameEvent (event objects held in a queue that can preform appropriate work and send appropriate responses to clients)

Now, my issue is that both Client and GameEvent (and probably Environment, once we get to it) need a reference to the Game object that they belong to.

Clients need to add GameEvent's to the Game's work queue.

GameEvent's need to access other game data (other Client's Ships, Environment).

Is there a better/more conventional method instead of having these objects store a local reference to their Game? What about declaring all of Game's methods as static? We only need to handle one game at a time, so there wouldn't ever be more than one instance of Game...

I'm sure there is a convention for a system with one central object that has many helper objects that need to reference it.

Did you consider using a Dependency Injection framework like Guice? There you have config classes called "modules", where you bind your interface Game to an implementation (you can decide if you want a singleton or new instances). A Client class would look like

public class Client {
   private final Game game;

   @Inject
   public Client(Game game) {
      this.game = game; 
   }   

   ... 
}

You can construct this class as usual, providing a Game instance (eg for testing, using a mock Game class). But if you let Guice create this instance for you (which doesn't need to be directly, it works as well if another class injects Client), you get automatically the instance specified in your Guice module.

I know it takes some time to wrap your head around that concept, but I can confirm that this leads to cleaner, more flexible code.

If there's really only logically ever one instance, you can use a singleton. The canonical form of a singleton is:

public enum Singleton {
    INSTANCE;

    // fields and methods here
}

That way, you don't have to shoehorn everything into static methods (though, if you want to write static methods that reference INSTANCE , that's fine too). Any code that wants to access the singleton just uses Singleton.INSTANCE , and you don't have to pass it around if you don't want to.

Passing the reference around will keep your options open, it can still be a reference to an actual static object. Also the concept of a request context might be useful, an object that holds all references needed to process a single request, and you pass that around.

Check out Inversion of Control (IOC) and containers.

That way, in your Client and GameEvent classes, whenever you need access to the Game, you just do something like:

var game = IoC.Resolve<Game>(); 

And then use the game instance methods...

There's nothing wrong with passing references to a constructor and storing them. You should also introduce an interface that will mediate access between your Game and your client and environment objects.

I would strongly advise not using a singleton or a static class in your design. There are lots of reasons for this, but the one that will probably affect you most immediately is that it makes things very hard to test. At testing time, there will probably be more than one instance of Game.

A common convention to ameliorate having one big central object with lots of helper objects is to try to avoid one big central object. You note that there are different clients of 'Game' with different needs. Maybe your interface on Game is too wide.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM