如何在不复制代码的情况下消除耦合



我正在为一款多人游戏制作服务器,并且在弄清楚如何使逻辑模块化方面存在一些问题。当玩家移动时,我想要更新对象,更新数据库记录并向所有连接的客户端发出移动。现在我是这样做的:

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。更新和套接字。在每次更新播放器对象时,在游戏循环中发出。

正确的做法是什么?

如果Player"类"的唯一目的是处理这些类型的更新,那么我看到的唯一真正的改变是参数化玩家代码,而不是让它直接包含db和socket。但这是一个非常小的变化,因为require调用已经在某种程度上解耦了。

我可以看到三种方法:

  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});
    };
    

但是你最终在每个对象中都有这些引用。

  • 使它们成为创建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);
    
  • 使PlayerEventEmitter发出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});
    });
    
  • 使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)让你的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);
    });
    

    我本人对看到在这种情况下解耦的建议非常感兴趣。我个人的建议

    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]();
        }
    }
    

    如果您希望不同的Players对应不同的observers,您可以选择

    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]();
        }
    

    相关内容

    最新更新