node seneca

处理新需求而无需重构 (Handling new requirements without refactoring)

Part 1 of this series talked about defining and calling microservices using Seneca. A handful of services were created to return all legal moves of a lone chess piece on a chessboard. The series continues in Part 3.

本系列的第1部分讨论了使用Seneca定义和调用微服务。 创建了一些服务,以返回棋盘上一个单独的棋子的所有合法举动。 该系列在第3部分中继续。

快速回顾: (A quick review:)

  • Seneca services are identified by a pattern consisting of role and cmd properties. Additional properties can be added to the pattern as well.

    Seneca服务由包含rolecmd属性的模式标识。 附加属性也可以添加到模式中。

this.add({role: "movement",cmd: "legalMoves"   //, otherProp: value, ...}, (msg, reply) => {...}
  • Services also have an implementation that takes a msg object and a reply callback. Themsg object contains the pattern properties in addition to all other data sent to the service.

    服务还具有一个接受msg对象和回复回调的实现。 msg对象除了发送到该服务的所有其他数据之外,还包含模式属性。

  • Seneca.act()is used to indirectly invoke a service. The act method takes an object and a callback function. The object contains the role, cmd, and other properties that comprise the message to the service.

    Seneca.act()用于间接调用服务。 act方法带有一个对象和一个回调函数。 该对象包含rolecmd和其他属性,这些属性构成向服务发送的消息。

seneca.act({role: "movement",cmd: "legalMoves",piece: p,board: board}, (err, msg) => {
  • When an action could be handled by more than one service that matches the pattern, the service with the most specific pattern match will be invoked.

    当一个动作可以由多个与模式匹配的服务来处理时,具有最特定模式匹配的服务将被调用。

There were a handful of services defined in the first part of this series. One of threerawMoves services took a piece and its position as parameters and returned 15 x 15 movement mask. These were truncated to an 8 x 8 board using alegalSquares service. The result was that the services together can return all the legal moves of any piece on any legal square of the otherwise empty chessboard.

在本系列的第一部分中定义了一些服务。 三个rawMoves服务之一将一块及其位置作为参数,并返回15 x 15移动蒙版。 使用legalSquares服务将它们截断成8 x 8的木板。 结果是这些服务可以一起返回原本空的棋盘的任何合法广场上的任何棋子的所有合法移动。

微服务和技术债务 (Microservices and Technical Debt)

One of the motivations for microservices is to reduce technical debt. Every project has deadlines and, as they loom larger, expediency often trumps quality. FIXME and TODO comments litter the source code after a while. Those comments identify technical debt that “someday” will be taken care of.

微服务的动机之一是减少技术债务 。 每个项目都有最后期限,并且随着期限的增加,权宜之计往往比质量重要。 FIXME和TODO注释过了一会儿会乱码源代码。 这些评论指出了“某天”将得到解决的技术债务。

总有一天永远不会来 (Someday never comes)

Microservices focus on functional decomposition and loose coupling. Neither of those are new ideas, but it is a rethinking about how to implement those concepts. A microservice should be small, single-purposed, and extensible. Extending a service can happen with few or no side-effects. A new service can extend an existing service, and neither the old service nor the client that once called it will know the service implementation changed. Less refactoring of classes, methods, method signatures, process flow… all this makes it easier to deal with dreaded TD.

微服务专注于功能分解和松散耦合。 这些都不是新想法,但这是对如何实现这些概念的重新思​​考。 微服务应小型,单一用途且可扩展。 扩展服务几乎没有副作用。 新服务可以扩展现有服务,并且旧服务或曾经调用它的客户端都不会知道服务实现已更改。 更少的类,方法,方法签名,过程流的重构……所有这些使得处理可怕的TD更容易。

回到游戏进行中… (Back to the game in progress…)

Moving a single chess piece around a lonely board is not really all that entertaining. In a real chess game the chessboard is shared with friendly and hostile pieces, and these impact each other’s movement.

在一个孤独的棋盘上移动单个棋子并不是真正有趣的事情。 在真正的国际象棋游戏中,国际象棋棋盘与友善而敌对的棋子共享在一起,这些棋子相互影响着对方的动作。

Right now I have alegalSquares service which can be the basis of a more completelegalMovesservice. If you recall, the legalSquares service would invoke a rawMovesservice, then remove all the ‘bad’ squares that didn’t belong on a chessboard.

现在,我有一个legalSquares服务,可以作为更完整的legalMoves服务的基础。 如果您还记得的话, legalSquares服务将调用rawMoves服务,然后删除所有不属于棋盘的“坏”正方形。

The new legalMoves service will take into account other pieces, something that legalSquares didn’t. This requires an extra parameter, one called board. The board is just going to be an array of ChessPiece instances, and will assume that the pieces on the board have already been checked for validity. For instance, two pieces don’t occupy the same square, pawns aren’t on the first rank, kings aren’t be next to each other, and so forth.

新的legalMoves服务将考虑其他部分,而legalSquares没有。 这需要一个额外的参数,称为boardboard将只是一个ChessPiece实例的数组,并将假定棋盘上的棋子已经过有效性检查。 例如,两个棋子不在同一广场上,棋子不在第一位,国王不在彼此之间,依此类推。

The following pattern will identify the service:

以下模式将标识服务:

'role: movement;cmd: legalMoves'

This pattern is a stringified version of JSON called jsonic; you can use a regular JSON object if you prefer. The message to the service will contain the pattern. It will also contain a ChessPiece instance that has a piece type such as ‘K’ing, ‘Q’ueen, ‘R’ook and board position (see algebraic notation). Later I’ll add to this class a piece color (White or Black) so that the service can tell friend from foe. But for now the service will assume all pieces are friendly.

此模式是JSON的字符串化版本,称为jsonic ; 您可以根据需要使用常规JSON对象。 发送给服务的消息将包含该模式。 它还将包含一个ChessPiece实例,其实例类型为“ K”,“ Q”,“ R”和棋盘位置(参见代数表示法)。 稍后,我将在该类中添加一块颜色(白色或黑色),以便该服务可以将敌人告诉朋友。 但就目前而言,该服务将假定所有部件都友好。

Since a friendly piece cannot be captured, it will restrict movement of other friendly pieces. Determining those restrictions is a bit of work. I made it harder for myself in the implementation of the rawMoves service… which brings me to:

由于无法捕获一个友善的棋子,它将限制其他友善棋子的移动。 确定这些限制需要一些工作。 在执行rawMoves服务时,我自己变得更加困难……这使我从事以下工作:

微服务不是万能药 (Microservices are not a Panacea)

If you design a service that retrieves or calculates information and doesn’t pass that data on up the chain, some service upstream may have to redo that work later. In my example, rawMoves returned an array of move objects (file and rank positions on the board). Let’s take the method that generates diagonal moves for a piece using the rawMoves service:

如果您设计的服务可以检索或计算信息, 但不会在链上传递该数据,则上游的某些服务可能必须稍后重做。 在我的示例中, rawMoves返回了一组移动对象(板上的文件和排名位置)。 让我们采用使用rawMoves服务为一块生成对角线移动的方法:

module.exports = function diagonal(position, range = 7) {var moves = [];const cFile = position.file.charCodeAt()const cRank = position.rank.charCodeAt();for (var i = 1; i < range + 1; i++) {moves.push({file: String.fromCharCode(cFile - i),rank: String.fromCharCode(cRank - i)});moves.push({file: String.fromCharCode(cFile + i),rank: String.fromCharCode(cRank + i)});moves.push({file: String.fromCharCode(cFile - i),rank: String.fromCharCode(cRank + i)});moves.push({file: String.fromCharCode(cFile + i),rank: String.fromCharCode(cRank - i)});}return moves;
}

At first glance, there’s nothing wrong with this. But, those fourmove.push operations actually operate along movement vectors. I could have constructed four movement vectors, then returned a list of moves by concatenating them, like so:

乍看之下,这没有什么错。 但是,这四个move.push操作实际上是沿着运动矢量进行操作的。 我本可以构造四个运动矢量,然后通过将它们连接起来返回一个运动列表,如下所示:

function diagonalMoves(position, range) {var vectors = [[], [], [], []];const cFile = position.file.charCodeAt()const cRank = position.rank.charCodeAt();for (var i = 1; i < range + 1; i++) {vectors[0].push({file: String.fromCharCode(cFile - i),rank: String.fromCharCode(cRank - i)});vectors[1].push({file: String.fromCharCode(cFile + i),rank: String.fromCharCode(cRank + i)});vectors[2].push({file: String.fromCharCode(cFile - i),rank: String.fromCharCode(cRank + i)});vectors[3].push({file: String.fromCharCode(cFile + i),rank: String.fromCharCode(cRank - i)});}const moves = Array.prototype.concat(...vectors)return moves;
}

As it stood, there was no point in doing this. But later on those vectors would have come in handy for truncating movements along diagonals (or ranks or files) when a friendly piece is in the way. Instead, I had to decompose the move list along vectors in services upstream — more work and inefficiency which you will see later.

就目前而言,这样做是没有意义的。 但是后来,当友好片段出现时,这些矢量将可以方便地沿对角线(或等级或文件)截断运动。 取而代之的是,我不得不沿着上游服务中的向量分解移动列表,这会增加工作量和效率,这将在以后看到。

The real flaw, though, was that I returned an array, rather than a data object. Data objects have properties that are extendable, not so arrays. As a consequence, all my upstream services depend on receiving a movement array, and only a movement array. No flexibility. I can’t now add a list of movement vectors in addition to a move list. But I could if I had returned an object from this method and the service that called it instead.

但是,真正的缺陷是我返回了一个数组,而不是一个数据对象。 数据对象具有可扩展的属性,而不是数组。 结果,我所有的上游服务都依赖于接收移动数组, 并且只有一个运动数组。 没有灵活性。 我现在不能添加除了运动向量的列表 到移动列表。 但是我可以从该方法和调用它的服务中返回一个对象。

Lesson learned? Consider returning data objects from your services. Have your upstream services work on parts of the data, but pass all data they receive back upstream. Exceptions to this rule will abound, of course.

学过的知识? 考虑从服务中返回数据对象。 让上游服务处理部分数据,但将它们接收的所有数据传回上游。 当然,会有很多例外。

和像这样的朋友一起... (With Friends like These…)

In Part 1, there was a service under the pattern:

在第1部分中,使用以下模式提供服务:

role:"movement",cmd:"legalSquares"

role:"movement",cmd:"legalSquares"

It returned all moves of an unimpeded piece. Since this will be the base service for determining legal moves on a populated chessboard, I’ll rename the cmdto legalMoves. Now I want to extend that to take into account friendly pieces that might be blocking a path of my chosen piece.

它返回了一块畅通无阻的动作。 由于这将是确定填充棋盘上法律动作的基本服务,因此我将cmd重命名为legalMoves 。 现在,我想扩展该范围,以考虑到可能会阻碍我选择的作品路径的友好作品。

扩展服务 (The extended service)

The service that extends role:"movement",cmd:"legalMoves" is… role:"movement",cmd:"legalMoves" !

扩展role:"movement",cmd:"legalMoves"是… role:"movement",cmd:"legalMoves"

Yep, it has the same service pattern as the service it calls. You may recall that services are identified by pattern, and so how it this going to work? When the program acts on role:"movement",cmd:"legalMoves", it will use the most recently defined service. But the new service has to call the formerlegalMoves service. That can be solved easily:

是的,它具有与其调用的服务相同的服务模式。 您可能还记得,服务是通过模式来标识的,那么它如何工作? 当程序执行role:"movement",cmd:"legalMoves" ,它将使用最新定义的服务。 但是新服务必须调用以前的legalMoves服务。 这很容易解决:

this.add({role: "movement",cmd: "legalMoves"}, (msg, reply) => {//returns unimpeded moves}this.add('role:movement,cmd:legalMoves', function (msg, reply) {this.
prior(msg, function (err, moves) {if (msg.board) {const boardMoves = legalMovesWithBoard(msg, moves);reply(err, boardMoves);return;}reply(err, moves);});});

This new service is able to call the former service by using the prior() method in Seneca. If no board parameter is supplied in the incoming msg object, then this service will just act as a pass-thru to the former service. But what if there is a board?

通过使用Seneca中的Priority prior()方法,此新服务可以调用以前的服务。 如果传入的msg对象中未提供board参数,则此服务将msg当前一个服务的直通。 但是,如果有董事会呢?

I’m not going to show a complete code listing here (see link below), but the gist of it is:

我不会在此处显示完整的代码清单(请参见下面的链接),但是要点是:

module.exports = function (msg, moves) {if (!msg.board) return moves;const blockers = moves.filter(m => {return (msg.board.pieceAt(m))})var newMoves = [];const pp = msg.piece.position;const rangeChecks = {B: diagonalChecks,R: rankAndFileChecks,K: panopticonChecks,Q: panopticonChecks,P: pawnChecks,N: knightChecks};var rangeCheck = rangeChecks[msg.piece.piece];// console.error(msg.piece.piece, rangeCheck.name)newMoves = moves.filter(m => {return rangeCheck(m, blockers, pp);})return newMoves;
}

Remember our old friend diagonalMoves from the rawMoves service? In order to do a range check on diagonals without handy vectors, the new legalMoves service calls this:

还记得我们来自rawMoves服务的老朋友diagonalMoves rawMoves吗? 为了对不带方便向量的对角线进行范围检查,新的legalMoves服务将其称为:

Ugly, no? I’d be happy if some algorithmically-inclined reader reduced this to two lines in the comments section. Three, even.

丑陋,不是吗? 如果某些有算法倾向的读者在评论部分将其减少到两行,我将感到高兴。 三,甚至。

So that takes care of friendly pieces. The next installment will deal with hostile pieces, which can be captured.

这样就可以处理友好的片段。 下一部分将处理可捕获的敌对碎片。

Full source code for this article can be found at GitHub.

可以在GitHub上找到本文的完整源代码。

翻译自: https://www.freecodecamp.org/news/follow-the-rules-with-seneca-ii-c22074debac/

node seneca

node seneca_使用Node.js和Seneca编写国际象棋微服务,第2部分相关推荐

  1. node seneca_使用Node.js和Seneca编写国际象棋微服务,第1部分

    node seneca (This is Part 1 of a three-part series [Part 2, Part 3]) (这是一个由三部分组成的系列文章的第1部分[ 第2 部分 , ...

  2. node seneca_使用Node.js和Seneca编写国际象棋微服务,第3部分

    node seneca Finishing up a three-part series on writing a rules engine with Seneca microservices. 完成 ...

  3. Seneca :NodeJS 微服务框架入门指南

    Seneca :NodeJS 微服务框架入门指南 原文:http://onmr.com/press/getting-started-seneca.html Seneca 是一个能让您快速构建基于消息的 ...

  4. Seneca:NodeJS 微服务框架入门(一)

    Seneca是什么? (1)官网是这样介绍的: Seneca is a microservices toolkit for Node.js. It helps you write clean, org ...

  5. 高质量 Node.js 微服务的编写和部署

    前几天在微信群做的一次分享,整理出来分享给大家,相关代码请戳 https://github.com/Carrotzpc/docker_web_app 微服务架构是一种构造应用程序的替代性方法.应用程序 ...

  6. js word 预览_Node.js微服务实践(二)

    基于Seneca 和 PM2构建 本章主要分为三个小节: 选择Nodejs的理由:将证明选择Node.js来构建的正确性.介绍使用Node.js时设计的软件栈. 微服务架构Seneca:关于Senec ...

  7. Node.js微服务 2 :基于Seneca和PM2构建Node.js微服务

    2.1 选择Node.js的理由 如今,Node.js已经成为国际上许多科技公司的首选方案.特别的,对于在服务器端需要非阻塞特性(例如Web Sockets)的场景,Node.js俨然成了最好的选择. ...

  8. 小程序 timestamp_通过构建Timestamp微服务应用程序来学习Node.js

    小程序 timestamp by Ayo Isaiah 通过Ayo Isaiah 通过构建Timestamp微服务应用程序来学习Node.js (Learn Node.js by building a ...

  9. Node微服务之Seneca的使用

    Seneca在Node微服务中的运用 1.seneca基本用法介绍 准备工作: npm i -S seneca npm i -S seneca-basic #匹配模式 /** demo.js **/ ...

最新文章

  1. 网易=4 ×(新浪 + 搜狐)
  2. 解析:为什么人工智能要用Python?
  3. React 在body上绑定事件以及阻止事件冒泡
  4. c语言实现ftp网络应用程序,使用C语言socket实现windows pc与ftp服务器通信---socket实现ftp客户端...
  5. java竖线分割_Java String类的Split以竖线作为分隔符
  6. C语言十五位正整数相加,二个超长正整数的相加
  7. Navicat for SQL Server Mac 版 SQL 创建工具
  8. console application
  9. html 文字 向上滚动代码,文字向上滚动代码
  10. sony a7 android,摄影 篇一:SONY A7R4利用ftp传输直传照片到安卓设备,完爆app体验
  11. 集成运放的电压跟随器有什么作用?
  12. hybrid 单臂路由
  13. In library(package, lib.loc = lib.loc,character.only = TRUE, there is no package called ‘kknn’
  14. ​手把手教你做个AR涂涂乐 ​
  15. 全球首届VueConf演讲PPT和视频免费公开
  16. 基于ZigBee和STM32的智能家居控制系统的设计与实现
  17. Mysql 为什么默认定义varchar(255) 而不是varchar(256)
  18. python网页登录钉钉_关于钉钉接口使用Python,Post 500报错
  19. emq查看状态“node emqx@127.0.0.1 not responding to pings”
  20. Mysql数据库经验总结

热门文章

  1. 【面试总结】2021Java春招面试经历
  2. [Swift]LeetCode74. 搜索二维矩阵 | Search a 2D Matrix
  3. BZOJ2597 WC2007剪刀石头布(费用流)
  4. rsync(六)命令中文手册
  5. SQL Server需要监控哪些计数器 ---指尖流淌
  6. django入门记录 2
  7. 转载 - 整数划分问题
  8. 在服务器端生成Excel文件然后从服务器下载的本地的代码
  9. ionic2开发的仿外卖点餐系统(Ionic2+Angular2
  10. caffe调用的一个例子