Иногда нашему актеру нужно реагировать по-разному в зависимости от его внутреннего состояния. Обычно получение какого-то определенного сообщения вызывает изменение состояния, которое, в свою очередь, меняет способ обработки последующих сообщений. Другое сообщение восстанавливает исходное состояние и, следовательно, способ, которым сообщения обрабатывались ранее. В предыдущей статье мы реализовали актер 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 = falsedef receive = {case RandomRequest =>preFetchIfAlmostEmpty()if(buffer.isEmpty) {backlog += sender} else {sender ! buffer.dequeue()}case RandomOrgServerResponse(randomNumbers) =>buffer ++= randomNumberswaitingForResponse = falsewhile(!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 ++= randomNumberscontext.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 и соседстве .