如何在不重复代码的情况下删除耦合?

时间:2015-05-17 10:58:34

标签: javascript node.js game-engine

我正在为多人游戏制作服务器,并且在确定如何制作逻辑模块时遇到一些问题。当玩家移动我想要更新对象时,更新数据库记录并将移动发送到所有连接的客户端。现在我这样做:

var socket = require('./socket');
var db = require('./db');

function Player(id, x, y) {
    this.id = id;
    this.x = x;
    this.y = y;
}

Player.prototype.move = function(x, y) {
    this.x = x;
    this.y = y;

    db.update({id: this.id}, {x: x, y: y});
    socket.emit('player moved', {x: x, y: y});
}

这将套接字和数据库紧密耦合到感觉错误的播放器对象。但是,每次更新播放器对象时,我都不想在游戏循环中执行db.update和socket.emit。

这样做的正确方法是什么?

3 个答案:

答案 0 :(得分:2)

如果Player"类"的唯一目的?是处理这些类型的更新,然后我看到的唯一真正的变化是参数化播放器代码而不是直接包含数据库和套接字。但这是一个非常小的变化,因为需求调用已经有点脱钩。

我可以看到三种方法:

  1. 将它们设为Player的参数:

    function Player(id, x, y, db, socket) {
        this.id = id;
        this.x = x;
        this.y = y;
        this.db = db;
        this.socket = socket;
    }
    
    Player.prototype.move = function(x, y) {
        this.x = x;
        this.y = y;
    
        this.db.update({id: this.id}, {x: x, y: y});
        this.socket.emit('player moved', {x: x, y: y});
    };
    
  2. 但是你最终会在每个对象中都有这些引用。

    1. 使它们成为创建Player构造函数

      的构建器函数的参数
      exports.buildPlayerClass = function(db, socket) {
      
          function Player(id, x, y) {
              this.id = id;
              this.x = x;
              this.y = y;
          }
      
          Player.prototype.move = function(x, y) {
              this.x = x;
              this.y = y;
      
              db.update({id: this.id}, {x: x, y: y});
              socket.emit('player moved', {x: x, y: y});
          };
      };
      

      然后在调用模块中使用:

      var socket = require('./socket');
      var db = require('./db');
      var Player = require('./player').buildPlayerClass(db, socket);
      
      // ...
      
      var p = new Player("some-id", 0, 0);
      
    2. Player EventEmitter发出move个事件:

      var EventEmitter = require('events').EventEmitter;
      
      function Player(id, x, y) {
          EventEmitter.call(this);
          this.id = id;
          this.x = x;
          this.y = y;
      }
      Player.prototype = Object.create(EventEmitter.prototype);
      Player.prototype.constructor = Player;
      
      Player.prototype.move = function(x, y) {
          this.x = x;
          this.y = y;
      
          this.emit('move', {p: this, x: x, y: y});
      };
      

      然后用法:

      var socket = require('./socket');
      var db = require('./db');
      
      // ...
      
      var p = new Player("some-id", 0, 0);
      p.on('move', function(e) {
          db.update({id: e.p.id}, {x: e.x, y: e.y});
          socket.emit('player moved', {x: e.x, y: e.y});
      });
      
    3. Player使用常用事件发射器:

      function Player(id, x, y, emitter) {
          this.id = id;
          this.x = x;
          this.y = y;
          this.emitter = emitter;
      }
      
      Player.prototype.move = function(x, y) {
          this.x = x;
          this.y = y;
      
          this.emitter.emit('move', {p: this, x: x, y: y});
      };
      

      然后用法:

      var playerEvents = new EventEmitter();
      playerEvents.on('move', function(e) {
          db.update({id: e.p.id}, {x: e.x, y: e.y});
          socket.emit('player moved', {x: e.x, y: e.y});
      });
      
      // ...
      
      var p1 = new Player("p1", 0, 0, playerEvents);
      var p2 = new Player("p2", 0, 0, playerEvents);
      

答案 1 :(得分:1)

我会做两件事:

1)让你的Player类发出事件。这使您可以在将来添加更多触发器,而无需修改您的Player类

2)使用pos对象来保存x和y值。这使代码更简洁。

// In Player file
var EventEmitter = require('events').EventEmitter;

function Player(id, pos) {
  this.id = id;
  this.pos = pos;
}

Player.events = new EventEmitter();

Player.prototype.move = function (pos) {
  Player.events.emit('move', this);
};

module.exports = Player;

// In database file...
var db = require('./db');
var Player = require('./Player');

Player.events.on('move', function (player) {
  db.update({id: player.id}, player.pos);
});

// In socket file...
var socket = require('./socket');
var Player = require('./Player');

Player.events.on('move', function (player) {
  socket.emit('player moved', player.pos);
});

答案 2 :(得分:0)

我自己非常有兴趣看到在这种情况下脱钩的建议。我个人的建议

Player.prototype.observers = [];
Player.prototype.observers.push(
      function(){
          if(conditionToUpdateDb){
              db.update({id: this.id}, {x: this.x, y: this.y});
          }
      });
Player.prototype.observers.push(
      function(){
          if(conditionToEmit){
              socket.emit('player moved', {x: this.x, y: this.y});
          }
      });

然后

Player.prototype.move = function(x, y) {
    this.x = x;
    this.y = y;

    for(key in this.observers){
        this.observers[key]();
    }
}

如果您希望针对不同的observers使用不同的Players,则可以选择

function Player(id, x, y, observers) {
    this.id = id;
    this.x = x;
    this.y = y;
    this.observers = observers;
}

然后

for(key in this.observers){
        this.observers[key]();
    }