Автор оригинала: FreeCodeCamp Community Member.
Обработка новых требований без рефакторинга
Часть 1 Из этой серии говорили о определении и вызове микросервисов с использованием Seneca. Были созданы несколько услуг для возврата всех юридических ходов одинокого шахматной кухни на шахматной доске. Серия продолжается в Часть 3 Отказ
Быстрый обзор:
- Seneca Services определяется схеме, состоящим из
рольиCMDхарактеристики. Дополнительные свойства также могут быть добавлены к шаблону.
this.add({
role: "movement",
cmd: "legalMoves" //, otherProp: value, ...
}, (msg, reply) => {...}- Услуги также имеют реализацию, которая занимает
Msgобъект и обратный вызов ответа.MsgОбъект содержит свойства шаблонов в дополнение ко всем остальным данным, отправленным на службу. Seneca.act ()используется для косвенного вызова услуги.АктМетод принимает объект и функцию обратного вызова. Объект содержитроль,CMDи другие свойства, которые составляют сообщение на службу.
seneca.act({
role: "movement",
cmd: "legalMoves",
piece: p,
board: board
}, (err, msg) => {- Когда действие может быть обработано более чем одной услугой, которая соответствует шаблону, сервис с Самый специфический шаблон матча будет вызван.
Было несколько услуг, определенных в первая часть из этой серии. Один из трех Rawphoves Услуги взяли кусок и его положение в качестве параметров и вернули 15 х 15 маска движения. Они были усечены до доски 8 х 8, используя LegalSquares услуга. Результатом заключалась в том, что услуги вместе могут вернуть все юридические ходы любых произведений на любой юридической площади в противном случае пустой шахматной доски.
Микросервисы и технические долги
Одним из мотивов для микровидсов является Уменьшить техническую задолженность Отказ У каждого проекта есть сроки, и, поскольку они стали больше, целесообразность часто превосходит качество. Fixme и ToDo комментирует помет исходный код через некоторое время. Эти комментарии определяют техническую задолженность о том, что «когда-нибудь» будет заботиться о.
Когда-нибудь никогда не приходит
Микровентики сосредоточены на функциональном разложении и свободной муфте. Ни один из них не являются новыми идеями, но это переосмысление о том, как реализовать эти концепции. Микровиссу должен быть небольшой, однорегулируемый и расширяемый. Расширение сервиса может произойти с несколькими или без побочных эффектов. Новый сервис может продлить существующую службу, а также ни старый сервис, ни клиент, который, как только вы назвал, будет знать, что реализация услуг изменилась. Меньше рефакторинга классов, методов, методов подписей, процессов поток … Все это облегчает дело со страшным TD.
Вернуться к игре в процессе …
Перемещение одной шахматной части вокруг одинокой доски на самом деле не все, что интересно. В реальной шахматной игре шахматная доска разделяется с дружелюбными и враждебными фигурами, и они влияют на движение друг друга.
Прямо сейчас у меня есть LegalSquares Сервис, который может быть основой более полной Юридические комолы услуга. Если вы вспомните, LegalSquares Сервис будет вызывать Rawphoves Сервис, затем удалите все «плохие» квадраты, которые не принадлежали на площадке.
Новый Юридические комолы Сервис примет во внимание другие части, то, что LegalSquares не сделал. Это требует дополнительного параметра, который называется доска Отказ доска просто будет массивом Шахмата Экземпляры и предполагают, что кусочки на доске уже были проверены на действительность. Например, две части не занимают тот же квадрат, пешки не на первом ранге, короли не будут рядом друг с другом и так далее.
Следующий шаблон будет идентифицировать услугу:
'role: movement;cmd: legalMoves'
Этот шаблон представляет собой строговую версию json под названием jsonic ; Вы можете использовать обычный объект JSON, если вы предпочитаете. Сообщение на сервис будет содержать шаблон. Он также будет содержать экземпляр шахмат, который имеет кусок типа, такой как «K’ing», «Q’UEEN», R’OOK и POWER (см. Алгебраическую обозначение). Позже я добавлю в этот класс кусок цвета (белый или черный), чтобы сервис мог рассказать другу от врага. Но на данный момент сервис предполагает, что все кусочки дружелюбны.
Поскольку дружелюбный участок не может быть пойман, он ограничит движение других дружественных произведений. Определение этих ограничений – это немного работы. Я заставил себя сильнее для себя в реализации Rawphoves Сервис … Что приводит меня к:
Минервисты не являются панацеей
Если вы разработаете услугу, которая извлекает или рассчитывает информацию и не Передайте эти данные на цепочку, некоторые сервисные выше по потоку, возможно, придется повторить эту работу позже. В моем примере Rawphoves Вернул массив перемещения объектов (файл и рейтинговые позиции на доске). Давайте возьмем метод, который генерирует диагональные движения для части, используя Rawphoves услуга:
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;
}На первый взгляд в этом нет ничего плохого. Но эти четыре 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;
}Как он стоял, не было смысла делать это. Но позже эти векторы пригодились бы для усечения движений вдоль диагоналей (или рангов или файлов), когда дружелюбная часть в пути. Вместо этого мне пришлось разложить список переезда вдоль векторов в службах вверх по течению – больше работы и неэффективности, которые вы увидите позже.
Настоящий недостаток, однако, заключался в том, что я вернул массив, а не объект данных. Объекты данных имеют свойства, которые являются выдвижными, а не такими массивами. Как следствие, все мои сервисные услуги зависят от приема массива движения, и Только массив движения. Нет гибкости. Я не могу добавить список векторов движения Кроме того в список шагов. Но я мог бы, если бы я вернул объект из этого метода, и служба, которая вместо этого называется.
Урок выучен? Рассмотрите возможность возвращения объектов данных из ваших услуг. У вас есть ваши службы восходящие на части данных, но пройдите все данные, которые они получают обратно вверх по течению. Конечно, исключены из-за этого правила.
С друзьями, как эти …
В части 1 была услуга по узоре:
Роль: «Движение», CMD: «LegalSquares»
Это вернуло все движения беспрепятственной части. Поскольку это будет базовая служба для определения юридических ходов на населенной доске, я переименую CMD к Юридические комолы Отказ Теперь я хочу продлить это, чтобы принять во внимание дружеские произведения, которые могут блокировать путь моего выбранного деталя.
Расширенная служба
Служба, которая расширяется Роль: «Движение», CMD: «LegalMoves» … Роль: «Движение», CMD: «LegalMoves» !
Да, он имеет тот же сервисный шаблон, что и сервис, который он вызывает. Вы можете вспомнить, что услуги идентифицированы по образцу, и как это будет работать? Когда программа действует на Роль: «Движение», CMD: «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);
});
});Эта новая услуга может позвонить в прежнем сервисе, используя предыдущий () Метод в Сенеке. Если нет доска Параметр поставляется в Incoming Msg Объект, то эта услуга просто будет выступать в качестве прохода в прежнем сервисе. Но что, если есть доска?
Я не собираюсь показать полный список кодов здесь (см. Ссылку ниже), но гид 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;
}Помните нашего старого друга ДиагональПоказать от Rawphoves услуга? Для того, чтобы проверить диапазон на диагоналях без удобных векторов, новый Юридические комолы Сервис звонит это:
// m: proposed move
// blockers: blocking pieces
// pp: current piece position
function diagonalChecks(m, blockers, pp) {
let isGood = true;
for (const b of blockers) {
if (b.rank > pp.rank && b.file > pp.file) {
if (m.rank > pp.rank && m.file > pp.file) {
isGood = isGood && (m.rank < b.rank && m.file < b.file);
}
}
if (b.rank > pp.rank && b.file < pp.file) {
if (m.rank > pp.rank && m.file < pp.file) {
isGood = isGood && (m.rank < b.rank && m.file > b.file)
}
}
if (b.rank < pp.rank && b.file > pp.file) {
if (m.rank < pp.rank && m.file > pp.file) {
isGood = isGood && (m.rank > b.rank && m.file < b.file)
}
}
if (b.rank < pp.rank && b.file < pp.file) {
if (m.rank < pp.rank && m.file < pp.file) {
isGood = isGood && (m.rank > b.rank && m.file > b.file)
}
}
}
return isGood;
}Уродливый, нет? Я был бы счастлив, если какой-то алгоритмично наклоненный читатель уменьшил это на две строки в разделе комментариев. Три даже.
Так что позаботится о дружественных частях. Следующая рассрочка будет иметь дело с враждебными фигурами, которые могут быть захвачены.
Полный исходный код для этой статьи можно найти в Github Отказ