[英]correct module organization with node.js
I've built a small chess game in plain JavaScript, and in order to handle imports I just pushed them all into the html file, giving me the following: 我用普通的JavaScript构建了一个小型国际象棋游戏,为了处理导入,我将它们全部推送到html文件中,并提供了以下内容:
game.html game.html
.
.
<body id="body">
<script src="scripts/utils.js"></script>
<script src="scripts/pieces.js"></script>
.
.
.
<script src="scripts/game.js"></script>
</body>
</html>
Now I am starting to use Node.js and want to move the logic server-side and structure it properly using imports, but there are two files I'm having trouble with. 现在,我开始使用Node.js,并希望移动逻辑服务器端并使用导入对其进行适当的结构化,但是我遇到了两个问题。 The first one is 第一个是
utils.js utils.js
function getEnemy(player) {
return player===WHITE? BLACK : WHITE;
}
/**
* A generator to generate pieces from given fen and return them all in an array.
*/
function generatePieces(fen) {
var piece_array = [];
var ranks = fen.split(' ')[0].split('/');
for (var i=0; i < ranks.length; i++) {
var squares = ranks[i];
var rank = 8 - i;
var file = 1; // keeping track of the current file.
for (var j=0; j < squares.length; j++) {
if (Number(squares[j])) {
file += Number(squares[j]);
continue;
}
var color = (squares[j] === squares[j].toUpperCase()? WHITE : BLACK);
piece_array.push( generatePiece(squares[j], color, file, rank));
file +=1;
}
}
return piece_array;
}
function generatePiece(type, color, file, rank) {
var square = new Square(file, rank);
type = type.toUpperCase();
switch(type) {
case PAWN:
return new Pawn(square, color);
case KNIGHT:
return new Knight(square, color);
case KING:
return new King(square, color);
case BISHOP:
return new Bishop(square, color);
case ROOK:
return new Rook(square, color);
case QUEEN:
return new Queen(square, color);
}
}
/**
* generates a square from the first two numbers in an array.
* no validations are made.
* @param position_array an array whose first two elements are numbers.
* @returns {Square} a square with file equal to the first element and rank the second.
*/
function getSquareFromArray(position_array) {
return new Square(position_array[0], position_array[1]);
}
/******* Square class
* square object to encapsulate all these ugly arrays.
* @param file file of the new square.
* @param rank rank of the new square.
*/
function Square(file, rank) {
this.file = file;
this.rank = rank;
/**
* Checks if two squares have the same file and rank.
* @param other another square
* @returns {boolean} true if both squares are of the same position on the board.
*/
this.equals = function(other) {
return (this.file === other.file && this.rank === other.rank);
};
/**
* Returns a string version of the square, formatted as "file rank".
* @returns {string} string version of the square.
*/
this.toString = function() {
return String(file) + ' ' + String(rank);
};
/**
* returns a new square with the given file and rank added to this square's file and rank.
* @param file file to move the square
* @param rank rank to move the square
* @returns {Square}
*/
this.getSquareAtOffset = function(file, rank) {
file = Number(file)? file : 0;
rank = Number(rank)? rank : 0;
return new Square(this.file + file, this.rank + rank);
};
}
/**
* creates a new square object from a string of format '%d $d'
* @param string string in format '%d %d'
* returns square object
*/
function getSquareFromKey(string) {
values = string.split(' ');
return new Square(Number(values[0]), Number(values[1]));
}
/******* Move object
* Move object to encapsulate a single game move.
* Might be a bit of an overkill, but I prefer an array of moves over an array of arrays of squares.
* @param from A square to move from.
* @param to A square to move to.
*/
function Move(from, to) {
this.from = from;
this.to = to;
}
/******* SpecialMove object
* Move object to encapsulate a single game move.
* Might be a bit of an overkill, but I prefer an array of moves over an array of arrays of squares.
* @param moves array of moves to make
* @param removes array of squares to remove pieces from
* @param insertions array of triplets, square to insert, wanted piece type, and piece color.
*/
function SpecialMove(moves, removes, insertions) {
this.moves = moves? moves : [];
this.removes = removes? removes : [];
this.insertions = insertions? insertions : [];
}
Which I'm not sure how to properly export. 我不确定如何正确导出。 If I add each function/object individually to module.exports then for each of my objects using this module (which is pretty much any other object) I'll need to do something like this.utils = require('./utils');
如果我将每个函数/对象分别添加到module.exports中,则使用该模块为我的每个对象(几乎是其他任何对象),我都需要这样做this.utils = require('./utils');
and then call, for example, square = new this.utils.Square(file, rank);
然后调用例如square = new this.utils.Square(file, rank);
, which doesn't seem like all that good of a solution. ,这似乎并不是解决方案的全部优点。 Is there a nicer way of importing this file pretty much globally? 有没有更好的方法可以在全球范围内导入此文件?
The second is a list of objects for the different types of pieces pieces.js 第二个是不同类型碎片的对象列表pieces.js
var WHITE = 'w';
var BLACK = 'b';
var PAWN = 'P';
var KNIGHT = 'N';
var KING = 'K';
var BISHOP = 'B';
var ROOK = 'R';
var QUEEN = 'Q';
/********* Piece class
* The parent class of all piece types.
* @param square square of the piece.
* @param color color of the piece, should be either 'white' or 'black'.
* @constructor
*/
function Piece(square, color) {
this.square = square;
this.color = color;
}
/**
* Returns the path the piece needs in order to get to the given square, if it's not a capture.
* Returns null if the piece can't reach the square in one move.
* @param to target square.
*/
Piece.prototype.getPath = function(to) {
return null;
};
/**
* Returns the path the piece needs in order to capture in the given square.
* Returns null if the piece can't reach the square in one move.
* Implement this if the piece captures in a different way then moving (i.e. a pawn);
* @param to target square.
*/
Piece.prototype.getCapturePath = function(to) {
return this.getPath(to);
};
/**
* Compares the square of the piece with given square
* @returns {boolean} if the piece is at the given square.
*/
Piece.prototype.isAt= function(square) {
return (this.square.equals(square));
};
/******* Pawn class
* Holds the movement of the pawn, refer to Piece for explanations.
* @type {Piece}
*/
Pawn.prototype = Object.create(Piece.prototype);
Pawn.prototype.constructor = Pawn;
function Pawn(square, color) {
Piece.call(this, square, color);
this.startPosition = (color === WHITE? 2 : 7);
this.direction = (color === WHITE? 1 : -1);
this.type = PAWN;
}
.
.
.
/******* Knight class
* Holds the movement of the Knight, refer to Piece for explanations.
* @type {Piece}
*/
Knight.prototype = Object.create(Piece.prototype);
Knight.prototype.constructor = Knight;
function Knight(square, color) {
Piece.call(this, square, color);
this.type = KNIGHT;
}
.
.
.
/******* King class
* Holds the movement of the King, refer to Piece for explanations.
* @type {Piece}
*/
King.prototype = Object.create(Piece.prototype);
King.prototype.constructor = King;
function King(square, color, has_moved) {
Piece.call(this, square, color);
this.type = KING;
}
.
.
.
Where I'm not sure how to correctly import all different "classes" for use in other places. 我不确定如何正确导入所有其他“类”以在其他地方使用。 I thought making the generatePiece
function from util.js
the module.export
of this file, thus turning it into a sort of "piece generator". 我以为可以从util.js
中将generatePiece
函数module.export
该文件的module.export
,从而将其转变为一种“生成器”。 is that a good idea? 这是一个好主意吗?
Unfortunately, there doesn't seem to be a defacto way of doing things in node.js, I'll try in give a couple generalized solutions: 不幸的是,在node.js中似乎没有实际的处理方法,我将尝试给出一些通用的解决方案:
I thought making the generatePiece function from util.js the module.export of this file, thus turning it into a sort of "piece generator". 我认为可以从util.js中将generatePiece函数制作为该文件的module.export,从而将其转变为一种“生成器”。 is that a good idea? 这是一个好主意吗?
I think that using your modules as standalone utils is a very straightforward and elegant solution. 我认为将模块用作独立的utils是一个非常简单明了的解决方案。 It is easily unittestable and is constantly used in node.js projects. 它易于进行单元测试,并在node.js项目中不断使用。
. 。 If I add each function/object individually to module.exports then for each of my objects using this module (which is pretty much any other object) I'll need to do something like this.utils = require('./utils'); 如果我将每个函数/对象分别添加到module.exports中,则使用该模块为我的每个对象(几乎是任何其他对象),我都需要这样做。utils = require('./ utils') ; and then call, for example, square = new this.utils.Square(file, rank);, which doesn't seem like all that good of a solution. 然后调用例如square = new this.utils.Square(file,rank);,这似乎并不是解决方案的全部优点。 Is there a nicer way of importing this file pretty much globally? 有没有更好的方法可以在全球范围内导入此文件?
Instead of storing a require
d library as a property in your object you could just construct new squares through the global reference, which is commonly used: 除了可以将require
d库作为属性存储在对象中之外,您还可以通过常用的全局引用构造新的正方形:
utils = require('./utils'); and then call, for example,
utils = require('./utils'); and then call, for example,
square = new utils.Square(file, rank);` utils = require('./utils'); and then call, for example,
square = new utils.Square(file,rank);`
Or even subdivide your modules even further, maybe even create a pieces module, or a board module. 甚至可以进一步细分您的模块,甚至可以创建部件模块或板模块。
I try to create as many modules as make sense. 我尝试创建尽可能多的模块。 One benefit is that there are lots of libraries in node to mock along require
s. 好处之一是节点中有很多库可以模拟require
。 If you end up doing IO it could be important to mock that for your unittests. 如果您最终要进行IO,那么对单元测试进行模拟可能很重要。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.