2

I have a class called Room, with an array containing all the Player entities as one of its properties,

players = [];

In the Room class is a method that only returns the players who actually competed in the round.

// below method is called by the room's timer

var getPlayersWhoFinished = function() {
    playersWhoFinished = [];
    for (i = 0; i < players.length; i++) {
        if (players[i].isFinished()) {
            playersWhoFinished.push(players[i]);
        };
    };
    return playersWhoFinished;
}

So I know that I could just leave the above in the Room class as is, but I already have three other functions with more complex mapping, in an already large class (300+ lines). I don't understand how to encapsulate these sort of methods into other classes, as they're so closely related to the Room class and all the the Room reference to be sent to the appropiate users.

Modifying the above code and slotting it into Player class would sort of make sense to me, but the only way I can think of getting this to work is using a static method and sending the room object to it.

// players is now a member of the Player class

Player.getPlayersWhoFinished = function(room, players) {
    playersWhoFinished = [];
    for (i = 0; i < players; i++) {
        if (players[i].getRoom() == room) {
            playersWhoFinished.push(players[i]);
        }
    }
    return playersWhoFinished;
}

Anyway, this seems cumbersome and inefficent to me. I've really been struggling to figure out how to make my Room class lithe as possible.

4

2 回答 2

1

Consider splitting logic into Objects and Collections. It is similar to what backbone offers (Model, Collection).

As collections logic is usually specific to objects it contains, but have some shared functionality as well (simple iterations, filters and so on), you can create generic Collection, and then through Inheritance add more methods in order to fit your needs of that specific Object it stores.

So you would have your room:

function Room() {
  // ..
  this.players = new PlayerCollection();
  // ..
}

For collection I've added some 'bonus' methods, so it would look like:

function Collection() {
  this.list = [ ];
  this.length = 0;
}

// adds item
Collection.prototype.add = function(item) {
  this.list.push(item);
  this.length = this.list.length;
}

// removes by index
Collection.prototype.remove = function(index) {
  this.list.splice(index, 1);
  this.length = this.list.length;
}

// finds one item based on filter function, and triggers callback with item and index
Collection.prototype.findOne = function(fn, callback) {
  for(var i = 0, len = this.list.length; i < len; ++i) {
    var result = fn(this.list[i]);
    if (result) {
      return callback(this.list[i], i);
    }
  }
  return callback(null, -1);
}

// filters off 
Collection.prototype.filter = function(fn) {
  return this.list.filter(fn);
}

Then you would define PlayerCollection, that will have extra method just to filter off players who is finished:

function PlayerCollection() {
  Collection.call(this);
}
// some inheritance here
PlayerCollection.prototype = Object.create(Collection.prototype);
PlayerCollection.prototype.constructor = PlayerCollection;

// returns list of finished players
PlayerCollection.prototype.finished = function() {
  return Collection.prototype.filter.call(this, function(player) {
    return player.isFinished();
  });
}

You still can reuse that filter method, as it helps to create some bespoke queries.

Your room logic would look like:

var room = new Room();
// add some players to room
// ...
var finishedPlayers = room.players.finished(); // what you need

It looks clear and straight forward, as well keeps all collection logic away from Room, so you can simply reuse it all over your game code. And improving in one place - would improve it as a whole.

Dividing logic into abstracts like that, helps to scale your code and separate dependencies.
Bear in mind Browser support for filter and if you need -IE8, then get shim from here.

于 2013-07-29T16:44:41.623 回答
1

The getPlayersWhoFinished() method belongs to Room, which is the object that should track players. You also are performing a search in O(n) complexity every time you need to find finished players, which can be improved.

You could have a callback mean to be called each time a player finishes:

Player.prototype.onFinished = function() {
    this.getRoom().addFinished(this);
}

And then manage a private array in Room containing all the finished players:

function Room() {
    this._finishedPlayers = [];
}

Room.prototype.addFinished = function(player) {
    this._finishedPlayers.push(player);
}

Room.prototype.getPlayersWhoFinished = function() {
    return this._finishedPlayers;
}

As a side note, you should always declare variables with var, or else you will get them declared in the global scope (i.e. usually the window object).

于 2013-07-29T19:43:29.260 回答