Иногда нашему актеру нужно реагировать по-разному в зависимости от его внутреннего состояния. Обычно получение какого-то определенного сообщения вызывает изменение состояния, которое, в свою очередь, меняет способ обработки последующих сообщений. Другое сообщение восстанавливает исходное состояние и, следовательно, способ, которым сообщения обрабатывались ранее. В предыдущей статье мы реализовали актер waitingForResponse
на waitingForResponse
флага waitingForResponse
. Это излишне сложная и без того сложная логика обработки сообщений:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
var waitingForResponse = false def receive = { case RandomRequest = > preFetchIfAlmostEmpty() if (buffer.isEmpty) { backlog + = sender } else { sender ! buffer.dequeue() } case RandomOrgServerResponse(randomNumbers) = > buffer ++ = randomNumbers waitingForResponse = false while (!backlog.isEmpty && !buffer.isEmpty) { backlog.dequeue() ! buffer.dequeue() } preFetchIfAlmostEmpty() } private def preFetchIfAlmostEmpty() { if (buffer.size < = BatchSize / 4 && !waitingForResponse) { randomOrgClient ! FetchFromRandomOrg(BatchSize) waitingForResponse = true } } |
Не проще ли иметь два разных метода receive
один используется, когда мы ожидаем ответа от внешнего сервера ( waitingForResponse == true
), а другой — когда буфер заполнен достаточно, а запрос к random.org
еще не random.org
? В таких условиях методы become()
и unbecome()
очень полезны. По умолчанию метод receive
используется для обработки всех входящих сообщений. Однако, в любое время мы можем вызвать стал (), который принимает любой метод, соответствующий подписи получения, в качестве аргумента. Каждое последующее сообщение будет обрабатываться этим новым методом. Вызов unbecome()
восстанавливает оригинальный метод receive
. Зная эту технику, мы можем рефакторинг нашего решения выше:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
def receive = { case RandomRequest = > preFetchIfAlmostEmpty() handleOrQueueInBacklog() } def receiveWhenWaiting = { case RandomRequest = > handleOrQueueInBacklog() case RandomOrgServerResponse(randomNumbers) = > buffer ++ = randomNumbers context.unbecome() while (!backlog.isEmpty && !buffer.isEmpty) { backlog.dequeue() ! buffer.dequeue() } preFetchIfAlmostEmpty() } private def handleOrQueueInBacklog() { if (buffer.isEmpty) { backlog + = sender } else { sender ! buffer.dequeue() } } private def preFetchIfAlmostEmpty() { if (buffer.size < = BatchSize / 4 ) { randomOrgClient ! FetchFromRandomOrg(BatchSize) context become receiveWhenWaiting } } |
Мы извлекли код, отвечающий за обработку сообщения, пока мы random.org
ответа random.org
в отдельный метод receiveWhenWaiting
. Обратите внимание на вызовы become()
и unbecome()
— они заменили ненужный флаг waitForResponse. Вместо этого мы просто говорим: начиная со следующего сообщения, пожалуйста, используйте этот другой метод для обработки (станьте немного другим актером). Позже мы говорим: хорошо, давайте вернемся к исходному состоянию и получим сообщения, как вы привыкли (не получилось). Но самое важное изменение — это переход от одного большого метода к двум, гораздо меньшим и лучшим именам.
unbecome()
become()
и unbecome()
самом деле намного мощнее, поскольку они внутренне поддерживают стек приемных методов. Каждый вызов функции discardOld = false
become()
(с discardOld = false
в качестве второго параметра) помещает текущий метод получения в стек, а unbecome()
его и восстанавливает предыдущий. Таким образом, мы можем использовать become()
чтобы использовать несколько методов получения, а затем постепенно возвращаться ко всем изменениям. Кроме того, Akka также поддерживает шаблон конечного автомата , но об этом, возможно, в будущем.
Исходный код этой статьи доступен на GitHub в become-unbecome tag
.
Это был перевод моей статьи « Познаемый Акка: стать / негодным », первоначально опубликованной на scala.net.pl .
Ссылка: станьте / не станьте — узнайте об Akka от нашего партнера по JCG Томаша Нуркевича в блоге о Java и соседстве .