当前位置: 首页 > 编程日记 > 正文

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

node seneca

(This is Part 1 of a three-part series [Part 2, Part 3])

(这是一个由三部分组成的系列文章的第1部分[ 第2 部分 , 第3部分 ])

I’ve begun wrapping my head around microservices. Up to this time I regarded it as a scalability pattern and overlooked the functional programming principles behind it.

我已经开始全神贯注于微服务。 直到现在,我仍将其视为可伸缩性模式,而忽略了其背后的功能编程原理。

The rules of chess can be decomposed easily into microservices. They are neither random nor ambiguous, which is perfect for writing small, stateless services that deal with movements of various pieces.

国际象棋的规则可以轻松地分解为微服务。 它们既不是随机的,也不是模棱两可的,非常适合编写处理各种动作的小型无状态服务。

In this post, I’ll walk through several services I created that determine what the legal moves are for lone pieces on an empty chessboard. We’ll use the Seneca framework, a microservices toolkit for Node.js, because it’s intuitive and well documented.

在本文中,我将逐步介绍我创建的几个服务,这些服务将确定空白棋盘上的单个棋子的合法移动方式。 我们将使用Seneca框架 ,这是用于Node.js的微服务工具包,因为它直观且文档齐全。

设置塞内卡 (Setting up Seneca)

Seneca is a Node.js module that is installed using npm:

Seneca是使用npm安装的Node.js模块:

npm install seneca

npm install seneca

Also, we’ll rely on globally installed mocha/chai modules for the tests that will illustrate functionality.

此外,我们将使用全球安装的mocha / chai模块进行测试,以说明功能。

It’s actually not necessary to maintain an in-memory representation of a chessboard, just the pieces and their location on an 8x8 coordinate grid. Algebraic notation is commonly used to describe the coordinates on a chessboard, where the files are denoted by letters and the ranks by numbers:

实际上,不必维护棋盘的内存表示形式,只需保持棋子及其在8x8坐标网格上的位置即可。 代数符号通常用于描述棋盘上的坐标,其中文件用字母表示,等级用数字表示:

For the player who is White, the rightmost bottom corner is h1; for Black it is a8. A rook on b2, moving to square f2, would be denoted as Rb2-f2.

对于怀特的玩家,最右下角是h1; 对于Black,它是a8。 b2上的小车移动到平方f2,将表示为Rb2-f2。

原始动作 (Raw Moves)

I am defining raw moves as the moves a piece would make if unimpeded by other pieces or the edge of the board. That last bit may seem odd, but it allows me to construct a 15x15 movement mask, which is then truncated to fit the 8x8 board. A fellow named Procrustes came up with a similar idea ages ago.

我将原始动作定义为一块棋子在不受其他棋子或棋盘边缘阻碍时会做出的动作。 最后一点可能看起来很奇怪,但是它允许我构造一个15x15的移动蒙版,然后将其截短以适合8x8的电路板。 一个叫Procrustes的家伙很久以前也提出了类似的想法。

Kings, Queens, Bishops and Rooks move along diagonals and/or files, so I will use one service for the movements of those four pieces. Pawns have unique movement characteristics, so a special service will be used for them. The same goes for Knights, since they can jump over pieces and don’t move along files or ranks.

国王,皇后,主教和白嘴鸦沿着对角线和/或锉刀移动,因此我将使用一种服务来移动这四件作品。 典当具有独特的移动特性,因此将为其使用特殊服务。 骑士也是如此,因为他们可以跳过棋子并且不会沿着文件或等级移动。

For example, a rook can move 7 squares along any rank or file on an 15x15 board in which the rook is centered. Similar rules apply to bishop and queen. The king is limited to a one-square range in any direction(the exception is castling, which I will deal with in a future post).

例如,车子可以在车子居中放置的15x15板上沿任何等级或文件移动7个正方形。 类似的规则适用于主教和皇后。 国王在任何方向上的范围都限于一个正方形(例外是cast,我将在以后的文章中讨论)。

I will use a ChessPiece class to hold information about the type and location of each chess piece. It won’t play too important a role for now, but it will later when I expand the scope of the rules covered by the services.

我将使用ChessPiece类来保存有关每个棋子的类型和位置的信息。 目前,它的作用并不重要,但是稍后,当我扩展服务所涵盖的规则的范围时,它将起到作用。

第一次服务:白嘴鸦,主教,女王和国王的举动 (First service: Rook, Bishop, Queen and King moves)

In Seneca, services are invoked via role and cmd. The role is akin to a category, and cmd names a specific service. As we’ll see later, a service can be further specified by additional parameters.

在Seneca中,通过role调用服务 cmd 。 该role类似于类别, cmd命名特定的服务。 稍后我们将看到,可以通过其他参数进一步指定服务。

Services are added using seneca.add(), and invoked via seneca.act(). Let’s look at the service, first (from Movement.js):

服务使用seneca.add()添加,并通过seneca.act()调用。 首先,让我们看一下服务(来自Movement.js):

this.add({role: "movement",cmd: "rawMoves",}, (msg, reply) => {var err = null;var rawMoves = [];var pos = msg.piece.position;switch (msg.piece.piece) {case 'R':rawMoves = rankAndFile(pos);break;case 'B':rawMoves = diagonal(pos);break;case 'Q':rawMoves = rankAndFile(pos).concat(diagonal(pos));break;case 'K':rawMoves = rankAndFile(pos, 1).concat(diagonal(pos, 1))break;default:err = "unhandled " + msg.piece;break;};reply(err, rawMoves);});

Now let’s see how the test invokes the service (movesTest.js):

现在,让我们看看测试如何调用服务(movesTest.js):

var Ba1 = new ChessPiece('Ba1');seneca.act({role: "movement",cmd: "rawMoves",piece: Ba1}, (err, msg) => {...});

Note that in addition to role and cmd, there is a piece argument. This, along with the role and cmd, are properties of the msg argument received by the service. Before you can invoke the service, though, you must tell Seneca which services to use:

注意除了role cmd ,有piece 论据。 这以及rolecmd都是msg属性 服务收到的参数。 但是,在调用服务之前,您必须告诉Seneca使用哪些服务:

var movement = require(‘../services/Movement’)
const seneca = require('seneca')({log: 'silent'}).use(movement);

The raw moves for a bishop at square a1 are in the msg received back from the service:

主教在a1广场的原始举动在msg 从服务中收到的回馈

[ { file: ‘`’, rank: ‘0’ }, { file: ‘b’, rank: ‘2’ }, { file: ‘`’, rank: ‘2’ }, { file: ‘b’, rank: ‘0’ }, { file: ‘_’, rank: ‘/’ }, { file: ‘c’, rank: ‘3’ }, { file: ‘_’, rank: ‘3’ }, { file: ‘c’, rank: ‘/’ }, { file: ‘^’, rank: ‘.’ }, { file: ‘d’, rank: ‘4’ }, { file: ‘^’, rank: ‘4’ }, { file: ‘d’, rank: ‘.’ }, { file: ‘]’, rank: ‘-’ }, { file: ‘e’, rank: ‘5’ }, { file: ‘]’, rank: ‘5’ }, { file: ‘e’, rank: ‘-’ }, { file: ‘\\’, rank: ‘,’ }, { file: ‘f’, rank: ‘6’ }, { file: ‘\\’, rank: ‘6’ }, { file: ‘f’, rank: ‘,’ }, { file: ‘[‘, rank: ‘+’ }, { file: ‘g’, rank: ‘7’ }, { file: ‘[‘, rank: ‘7’ }, { file: ‘g’, rank: ‘+’ }, { file: ‘Z’, rank: ‘*’ }, { file: ‘h’, rank: ‘8’ }, { file: ‘Z’, rank: ‘8’ }, { file: ‘h’, rank: ‘*’ } ]

[{文件:'`',等级:'0'},{文件:'b',等级:'2'},{文件:'`',等级:'2'},{文件:'b',等级:'0'},{文件:'_',等级:'/'},{文件:'c',等级:'3'},{文件:'_',等级:'3'},{文件:“ c”,等级:“ /”},{文件:“ ^”,等级:“。” },{文件:'d',等级:'4'},{文件:'^',等级:'4'},{文件:'d',等级:'。 },{文件:']',等级:'-'},{文件:'e',等级:'5'},{文件:']',等级:'5'},{文件:'e' ,等级:'-'},{文件:'\\',等级:','},{文件:'f',等级:'6'},{文件:'\\',等级:'6' },{文件:'f',等级:','},{文件:'[',等级:'+'},{文件:'g',等级:'7'},{文件:'[' ,等级:“ 7”},{文件:“ g”,等级:“ +”},{文件:“ Z”,等级:“ *”},{文件:“ h”,等级:“ 8”}, {文件:'Z',等级:'8'},{文件:'h',等级:'*'}]

Note that there are some weird squares listed! These are the positions that “fall off” the 8x8 board and will be eliminated later by another service.

请注意,其中列出了一些奇怪的方块! 这些是“跌落” 8x8电路板的位置,以后将由另一服务消除。

刚才发生了什么? (What just happened?)

A service was defined with role=”movement” and cmd=”rawMoves”. When act() is later invoked, the parameters of the act request are matched against a service that handles those parameters (this is called the service’s pattern). As mentioned previously and as will be shown in the next example, role and cmd are not necessarily the only parameters that determine the service invoked.

定义了一个服务,其中的role=”movement”role=”movement”cmd=”rawMoves” 。 稍后调用act() ,会将act请求的参数与处理这些参数的服务进行匹配(这称为服务的模式 )。 如前所述,将在下一个示例中显示, role cmd 不一定是确定调用服务的唯一参数。

下一个服务:典当骑士 (Next services: Pawns and Knights)

Pawns move one square forward unless they are on their original square, in which case they can move one or two squares forward. There are other moves a pawn can make when it is not the lone piece on an empty board, but that’s for future consideration. Pawns alway start on the second rank, and can never move backwards.

典当除非向前移动一个正方形,否则它们会向前移动一个正方形,在这种情况下,它们可以向前或向后移动两个正方形。 当典当不是一块空板上的一块棋子时,它还可以采取其他措施,但是这有待将来考虑。 典当始终从第二等级开始,并且永远不会向后移动。

Knights move in an L-shape pattern. In our imaginary 15x15 board with the knight centered, there will always be eight possible moves.

骑士以L形移动。 在我们想象中的以骑士为中心的15x15棋盘中,总是会有八种可能的动作。

I’ll write two services (one for pawns, the other for knights) and place both in one module (SpecialMovements.js):

我将编写两个服务(一个用于典当,另一个用于骑士),并将两者都放在一个模块中(SpecialMovements.js):

module.exports = function specialMovement(options) {//...this.add({role: "movement",cmd: "rawMoves",isPawn: true}, (msg, reply) => {if (msg.piece.piece !== 'P') {return ("piece was not a pawn")}var pos = msg.piece.position;const rawMoves = pawnMoves(pos);reply(null, rawMoves);});this.add({role: "movement",cmd: "rawMoves",isKnight: true}, (msg, reply) => {if (msg.piece.piece !== 'N') {return ("piece was not a knight")}var rawMoves = [];var pos = msg.piece.position;rawMoves = knightMoves(pos);reply(null, rawMoves);});
}

See the isPawn and isKnight parameters in the services? The first object passed to Seneca add() is called the service pattern. What happens is that Seneca will invoke the service with the most specific pattern match. In order to invoke the right service, I need to add isPawn:true or isKnight:true to the act request:

isPawn isKnight 服务中的参数? 传递给Seneca add()的第一个对象称为服务模式 。 发生的情况是,Seneca将使用最特定的模式匹配来调用服务。 为了调用正确的服务,我需要添加 行为请求的isPawn:trueisKnight:true

var movement = require('../services/Movement')
var specialMovement = require('../services/SpecialMovement')const seneca = require('seneca')({log: 'silent'}).use(specialMovement)...var p = new ChessPiece('Pe2');seneca.act({role: "movement",cmd: "rawMoves",piece: p,
...isPawn: true}, (err, msg) => {...}...var p = new ChessPiece('Nd4');seneca.act({role: "movement",cmd: "rawMoves",piece: p,isKnight: true}, (err, msg) => {

Our rudimentary legal move service will just filter out all the square positions that are not on files a-h or ranks 1–8. The legal move service will be called directly with a ChessPiece instance as part of the service payload. The legal move service will then invoke the raw move service to get the movement mask. The mask will be truncated to the edges of the board, and the result will be the square positions that can legally be played.

我们基本的法律搬迁服务只会过滤掉不在文件ah或1–8中的所有方形位置。 合法移动服务将直接用ChessPiece实例作为服务有效负载的一部分进行调用。 然后,合法移动服务将调用原始移动服务以获取移动掩码。 遮罩将被截断到棋盘的边缘,结果是可以合法播放的正方形位置。

this.add({role: "movement",cmd: "legalSquares",}, (msg, reply) => {const isPawn = msg.piece.piece === 'P';const isKnight = msg.piece.piece === 'N';this.act({role: "movement",cmd: "rawMoves",piece: msg.piece,isPawn: isPawn,isKnight: isKnight}, (err, msg) => {const squared = [];msg.forEach((move) => {if (move.file >= 'a' && move.file <= 'h') {if (move.rank >= 1 && move.rank <= 8) {squared.push(move)}}})reply(null, squared);});})

The legalSquares service first invokes the rawMoves service. This gets us the 15x15 movement mask for whatever piece is passed via the msg parameter. It is important, though, that the right service is invoked by setting the isKnight or isPawn pattern field to true for either of those two pieces… if both are false, then the “regular” rawMoves service for K,Q,B,R will be invoked.

legalSquares 服务首先调用rawMoves 服务。 对于通过msg参数传递的任何片段,这都会为我们提供15x15的移动蒙版。 但是重要的是,通过设置isKnight来调用正确的服务 isPawn 对于这两个部分中的任何一个,pattern字段都为true…如果两者均为false,则“常规” rawMoves K,Q,B,R的服务将被调用。

Once the raw moves are retrieved, then the legalSquares service removes the invalid positions and returns what is left. So if I invoke the service with the piece at Na1, I get:

一旦检索到原始移动,则legalSquares 服务会删除无效的职位并返回剩余的职位。 因此,如果我在Na1处调用该服务,则会得到:

[ { file: ‘c’, rank: ‘2’ }, { file: ‘b’, rank: ‘3’ } ]

If instead I pass in Rd4, legalSquares returns:[ { file: ‘c’, rank: ‘4’ }, { file: ‘d’, rank: ‘5’ }, { file: ‘e’, rank: ‘4’ }, { file: ‘d’, rank: ‘3’ }, { file: ‘b’, rank: ‘4’ }, { file: ‘d’, rank: ‘6’ }, { file: ‘f’, rank: ‘4’ }, { file: ‘d’, rank: ‘2’ }, { file: ‘a’, rank: ‘4’ }, { file: ‘d’, rank: ‘7’ }, { file: ‘g’, rank: ‘4’ }, { file: ‘d’, rank: ‘1’ }, { file: ‘d’, rank: ‘8’ }, { file: ‘h’, rank: ‘4’ } ]

相反,如果我通过Rd4,则legalSquares返回:[{文件:'c',等级:'4'},{文件:'d',等级:'5'},{文件:'e',等级:'4 '},{文件:'d',等级:'3'},{文件:'b',等级:'4'},{文件:'d',等级:'6'},{文件:'f ',等级:'4'},{文件:'d',等级:'2'},{文件:'a',等级:'4'},{文件:'d',等级:'7'} ,{文件:'g',等级:'4'},{文件:'d',等级:'1'},{文件:'d',等级:'8'},{文件:'h',等级:“ 4”}]

which is a little harder to decipher, but contains all files along the 4th rank and all ranks along the d-file (trust me!).

有点难以理解,但包含第4级的所有文件和d文件的所有级(相信我!)。

That’s it for now! In a future post I’ll go over services that deal with friendly pieces impeding movement, then dealing with the potential capture of hostile pieces. Further services will handle rules for castling, en passant, check, checkmate, and stalemate.

现在就这样! 在以后的文章中,我将介绍一些服务,这些服务处理阻碍移动的友善物品,然后处理可能捕获敌对物品的问题。 进一步的服务将处理有关转换, 传递,检查,将死和僵持的规则。

All source code can be found here.

所有源代码都可以在这里找到。

Continue to Part 2 of this series.

继续本系列的第2部分 。

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

node seneca

相关文章:

Ubuntu中基于QT的系统网线连接状态的实时监视

1.必要准备 需包&#xff1a; #include <QNetworkInterface> 2.实现获取当前的网线连接状态 以下是自己在网络上搜到的一个解决方法&#xff0c;且没有加入iface.flags().testFlag(QNetworkInterface::IsRunning) 这一逻辑判断&#xff0c;经测试实时性极不可靠&#xff…

iOS 开发者账号 到期续费问题

https://blog.csdn.net/liruiqing520/article/details/104043221

[转载]Using ngOptions In AngularJS

http://odetocode.com/blogs/scott/archive/2013/06/19/using-ngoptions-in-angularjs.aspx?utm_sourcetuicool转载于:https://www.cnblogs.com/Benoly/p/4097213.html

graphql_GraphQL的稳步上升

graphqlToday GitHub announced that the next version of their API will use a new technology developed by Facebook called GraphQL.今天&#xff0c;GitHub宣布其API的下一版本将使用Facebook开发的一项名为GraphQL的新技术。 GraphQL may eventually come to replace t…

转: windows系统下mysql出现Error 1045(28000) Access Denied for user 'root'@'localhost'

windows系统下mysql出现Error 1045(28000) Access Denied for user rootlocalhost 转自 http://zxy5241.spaces.live.com/blog/cns!7682A3008CFA2BB0!361.entry 在windows操作系统安装MySQL数据库&#xff0c;碰到Error 1045(28000) Access Denied for user rootlocalhost 错误…

正则表达式的字符、说明和其简单应用示例

字符和其含义 字符       含义 \         转义字符&#xff0c;将一个具有特殊功能的字符转义为一个普通的字符 ^        匹配字符串的开始位置 $        匹配字符串的结束位置 *        匹配前面的0次或多次的子表达式        …

iOS 设置UILabel 的行间距

// // UILabelLineSpace.h//#import <UIKit/UIKit.h>NS_ASSUME_NONNULL_BEGINinterface UILabel (LineSpace)/**设置文本,并指定行间距param text 文本内容param lineSpacing 行间距*/ -(void)setText:(NSString*)text lineSpacing:(CGFloat)lineSpacing;endNS_ASSUME_N…

倦怠和枯燥_启动倦怠

倦怠和枯燥by Elie Steinbock埃莉斯坦博克(Elie Steinbock) 启动倦怠 (Start-up Burnout) Shabbat is the seventh day of the week. It starts on Friday night and ends on the following evening, Saturday. (A day starts in the evening for the Jews.) It’s also the J…

客户端处理包方法

不同包客户端的处理方法 对于那种事件类型的 连接上了&#xff0c;连接失败了&#xff0c;断开连接了 bool NGP::OnConnected() {std::lock_guard<std::mutex> lock(m_PktMutex);//加锁是因为runonce应该是另一个线程m_queFunctions.push(std::bind(&NGP::Connect2Se…

0011_练习题d1

__author__ qq593 #!/usr/bin/env python #-*- coding:utf-8 -*- #使用while循环输入1 2 3 4 5 6 8 9 10 a1 while True:print(a)if(a10):breakif (a7):a1continuea1 __author__ qq593 #!/usr/bin/env python #-*- coding:utf-8 -*- #求1-100所有数的和 a1 sum00 while(a<…

iOS 仿微信灵活添加标签

iOS 仿微信灵活添加标签 原作者的github 地址 喜欢的点赞 https://github.com/DreamFlyingCow/TTTags 效果如下&#xff0c;iOS 13 访问私有属性 会崩溃&#xff0c;自己修改一下即可 TTTagView.m 文件修改如下 我的备份&#xff1a;https://github.com/AlexanderYeah/TTTa…

css 倒三角_倒三角结构:如何管理大型CSS项目

css 倒三角by Luuk GruijsLuuk Gruijs着 倒三角结构&#xff1a;如何管理大型CSS项目 (The Inverted Triangle Architecture: how to manage large CSS Projects) You’re assigned a small task to fix some little styling issues here and there. You’ve found the correc…

列举一些常见的系统系能瓶颈 Common Bottlenecks

http://www.nowamagic.net/librarys/veda/detail/2408在 Zen And The Art Of Scaling - A Koan And Epigram Approach 一文中&#xff0c; Russell Sullivan 提出一个很有趣的设想&#xff1a;一共有20种经典的瓶颈。这听起来就像只有20种基本的故事情节&#xff08;20 basic s…

Zeal 离线API文档浏览器

zeal是一个windows上的开源的离线文档浏览工具&#xff0c;基于docset格式&#xff0c;可以兼容全部dash的文档。zeal没有代码片段管理的功能&#xff0c;只提供文档浏览功能&#xff0c;不过windows下的用户可算是有的用了。dash目前只提供mac上的版本&#xff0c;作者说有往w…

iOS scrollToItemAtIndexPath 无效的解决方案

在UITableview中放置的UICollectionView,然后设置滚动没有效果scrollToItemAtIndexPath - (void)layoutSubviews {[self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:self.selectedIdx inSection:0] atScrollPosition:UICollectionViewScrollPositio…

机器学习编程语言_我应该选择哪种编程语言? 我应该专注于前端吗? 后端? 机器学习?...

机器学习编程语言by Preethi Kasireddy通过Preethi Kasireddy 我应该选择哪种编程语言&#xff1f; 我应该专注于前端吗&#xff1f; 后端&#xff1f; 机器学习&#xff1f; (What programming language should I pick? Should I focus on front-end? Back-end? Machine l…

spdlog源码阅读 (1): sinks

0. spdlog简单介绍 spdlog 是一个快速的 C 日志库&#xff0c;只包含头文件&#xff0c;兼容 C11。项目地址 特性: 非常快只包含头文件无需依赖第三方库支持跨平台 - Linux / Windows on 32/64 bits支持多线程可对日志文件进行循环输出可每日生成日志文件支持控制台日志输出可选…

什么样的程序员才算成熟? 让程序员认清自己的所处的阶段

http://www.nowamagic.net/librarys/veda/detail/1450程序员在经历了若干年编程工作之后&#xff0c;很想知道自己水平到底如何&#xff1f;自己是否已经成为成熟的程序员&#xff1f;虽然程序员会对自己有一个自我评价&#xff0c;但是&#xff0c;自己的评价和社会的评价、专…

iOS访问系统日历 添加提醒事件

1 添加隐私请求提示 Privacy - Calendars Usage Description 2 代码 #import <EventKit/EventKit.h> // 添加提醒事件 - (void)addEventWithTimeStr:(NSString *)timeStr title:(NSString *)title planId:(NSString *)planId {EKEventStore *store [[EKEventStore al…

数据分析从头学_数据新闻学入门指南:让我们从头开始构建故事

数据分析从头学by Mina Demian由Mina Demian 数据新闻学入门指南&#xff1a;让我们从头开始构建故事 (A Beginner’s Guide to Data Journalism: Let’s Build a Story From Scratch) This is an introductory guide on how to produce the beginnings of a piece of data jo…

Comparator 和 Comparable

Comparator 和 Comparable 比较 Comparable是排序接口&#xff1b;若一个类实现了Comparable接口&#xff0c;就意味着“该类支持排序”。而Comparator是比较器&#xff1b;我们若需要控制某个类的次序&#xff0c;可以建立一个“该类的比较器”来进行排序。 我们不难发现&…

朴素贝叶斯算法的python实现

朴素贝叶斯 算法优缺点 优点&#xff1a;在数据较少的情况下依然有效&#xff0c;可以处理多类别问题 缺点&#xff1a;对输入数据的准备方式敏感 适用数据类型&#xff1a;标称型数据 算法思想&#xff1a; 朴素贝叶斯比如我们想判断一个邮件是不是垃圾邮件&#xff0c;那么…

iOS 加载本地和网络gif 图片类扩展

https://github.com/AlexanderYeah/GifKuoZhan [self.meiXueImgView showGifImageWithData:[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:"美" ofType:"gif"]]];

arkit与现实世界距离比_如何使用ARKit和Pusher构建实时增强现实测量应用程序

arkit与现实世界距离比by Esteban Herrera由Esteban Herrera 如何使用ARKit和Pusher构建实时增强现实测量应用程序 (How to Build a Real-Time Augmented Reality Measuring App with ARKit and Pusher) Augmented reality (AR) is all about modifying our perception of the…

Servlet 3.0 新特性概述

Servlet 3.0 新特性概述 Servlet 3.0 作为 Java EE 6 规范体系中一员&#xff0c;随着 Java EE 6 规范一起发布。该版本在前一版本&#xff08;Servlet 2.5&#xff09;的基础上提供了若干新特性用于简化 Web 应用的开发和部署。其中有几项特性的引入让开发者感到非常兴奋&…

MVC、MVP、MVVM

MVC、MVP、MVVM这些模式是为了解决开发过程中的实际问题而提出来的&#xff0c;目前作为主流的几种架构模式而被广泛使用。 一、MVC&#xff08;Model-View-Controller&#xff09; MVC是比较直观的架构模式&#xff0c;用户操作->View&#xff08;负责接收用户的输入操作&a…

iOS 计算两个日期之间的差值

NSDateFormatter *dateFomatter [[NSDateFormatter alloc] init];dateFomatter.dateFormat "yyyy-MM-dd HH:mm";// 当前时间字符串格式NSDate *planDate [dateFomatter dateFromString:[model.PlanTime substringToIndex:16]];// 当前日历NSCalendar *calendar […

unity水管工_我是如何从30岁的管道工转变为32岁的Web开发人员的

unity水管工by Rick West由里克韦斯特(Rick West) 我是如何从30岁的管道工转变为32岁的Web开发人员的 (How I transformed from a 30-year-old plumber into a 32-year-old web developer) Friends often ask me why I decided to give up a solid, well-paying job like plum…

netty集成ssl完整参考指南(含完整源码)

虽然我们在内部rpc通信中使用的是基于认证和报文头加密的方式实现安全性&#xff0c;但是有些时候仍然需要使用SSL加密&#xff0c;可能是因为对接的三方系统需要&#xff0c;也可能是由于open的考虑。中午特地测了下netty下集成ssl的功能&#xff0c;关于ssl的握手过程以及jav…

呼叫中心的服务水平管理

对企业来讲&#xff0c;呼叫中心是企业的窗口&#xff0c;呼叫中心为客户提供服务&#xff0c;是企业提升品牌形象、建立客户忠诚度的最佳通道。因此&#xff0c;呼叫中心的服务水平对于企业来说意义重大&#xff0c;相应的服务水平管理更是企业必不可少的管理之一。 “客户至上…