如何在不复制代码的情况下去除耦合?
How to remove coupling without duplicating the code?
我正在为多人游戏制作服务器,但在弄清楚如何使逻辑模块化时遇到了一些问题。当玩家移动时,我想更新对象,更新数据库记录并将移动发送到所有连接的客户端。现在我是这样做的:
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。
这样做的正确方法是什么?
如果 Player
"class" 的唯一目的是处理这类更新,那么我看到的唯一真正的改变是参数化播放器代码,而不是让它直接包含数据库和套接字。但这是一个非常小的变化,因为 require 调用已经有些解耦了。
我可以看到三种方法:
将它们设为 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);
使 Player
成为发出 move
事件的 EventEmitter
:
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);
我本人非常有兴趣看到在这种情况下解耦的建议。我的个人建议
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]();
}
我会做两件事:
1) 让您的播放器 class 发出事件。这使您可以在将来添加更多触发器而无需修改您的播放器 class
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);
});
我正在为多人游戏制作服务器,但在弄清楚如何使逻辑模块化时遇到了一些问题。当玩家移动时,我想更新对象,更新数据库记录并将移动发送到所有连接的客户端。现在我是这样做的:
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。
这样做的正确方法是什么?
如果 Player
"class" 的唯一目的是处理这类更新,那么我看到的唯一真正的改变是参数化播放器代码,而不是让它直接包含数据库和套接字。但这是一个非常小的变化,因为 require 调用已经有些解耦了。
我可以看到三种方法:
将它们设为
的参数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);
使
Player
成为发出move
事件的EventEmitter
: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);
我本人非常有兴趣看到在这种情况下解耦的建议。我的个人建议
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]();
}
我会做两件事:
1) 让您的播放器 class 发出事件。这使您可以在将来添加更多触发器而无需修改您的播放器 class
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);
});