앞서 배운 내용에 대한 연습 문제를 만들었다.
사실 내가 만든 건 아니고, 강사님이 만드신거지만 설명과 코드를 같이 보니 이해가 조금 되는 것 같다.
첫 번째 연습 문제에서는 내부 변수를 가진 '카운터 액터'를 생성하여 증가, 감소, 출력의 기능을 하도록 만들었다.
두 번째 연습 문제는 은행 계좌를 액터로 만드는 것이였다. 출금, 입금, 명세서 메시지를 수신하여 성공과 실패에 대한 메시지를 출력하도록 했다.
계좌는 내부 변수를 사용해 자금을 유지하였고, 입금 및 출금 금액에 따라 내부 자금을 변경하였다.
모든 메시지 전송은 항상 비동기적이고 논블로킹 이므로 여러 메시지가 서로 중첩되지 않는지,
내부 상태 변경 시 동기화 처리가 필요 없는지에 대한 의문 제기가 늘 필요하다.
아래의 코드는 어제 작성한 게시물의 코드와 이어진다.
추가적으로 정리한 내용은 코드 내부의 주석에 있다.
/**
* 연습 문제들
*
* 1. 카운터 액터
* - 증가
* - 감소
* - 출력
*
* 2. 액터로서의 은행 계좌
* 받는 메시지들
* - 금액 입금
* - 금액 출금
* - 잔액 조회
* 응답 메시지들
* - 성공
* - 실패
*
* 다른 종류의 액터와 상호 작용
*/
// DOMAIN of the counter
// Akka 는 보통 액터의 동반객체에 메시지를 만들어 사용함
// 액터가 지원하는 메시지 등을 컴패니언에 넣는 것이 좋음
object Counter {
case object Increment
case object Decrement
case object Print
}
class Counter extends Actor {
import Counter._
var count = 0
// 메시지 핸들러에는 모든 메시지의 케이스에 대한 사례가 있어야 함
override def receive: Receive = {
case Increment => count += 1
case Decrement => count -= 1
case Print => println(s"[counter] My current count is $count")
}
}
// 카운터 도메인에서 요소들을 가져와야 하므로 임포트
import Counter._
val counter = system.actorOf(Props[Counter], "myCounter")
(1 to 5).foreach(_ => counter ! Increment)
(1 to 3).foreach(_ => counter ! Decrement)
counter ! Print
// 은행 계좌에 대한 동반 객체, 내부에는 동반 메시지
object BankAccount {
case class Deposit(amount: Int)
case class Withdraw(amount: Int)
case object Statement
case class TransactionSuccess(message: String)
case class TransactionFailure(reason: String)
}
class BankAccount extends Actor {
import BankAccount._
var funds = 0
// 케이스들과 은행 계좌가 지원할 메시지의 수를 입력해야 함(입금, 출금, 명세서)
// 거래가 유효한지 여부를 판단하여 메시지 출력
override def receive: Receive = {
case Deposit(amount) =>
if (amount < 0) sender() ! TransactionFailure("invalid deposit amount")
else {
funds += amount
sender() ! TransactionSuccess(s"successfully deposited $amount")
}
case Withdraw(amount) =>
if (amount < 0) sender() ! TransactionFailure("invalid withdraw amount")
else if (amount > funds) sender() ! TransactionFailure("insufficient funds")
else {
funds -= amount
sender() ! TransactionSuccess(s"successfully withdrew $amount")
}
case Statement => sender() ! s"Your balance is $funds"
}
}
// 은행 계좌 액터와 상호작용하는 사람 클래스
object Person {
case class LiveTheLife(account: ActorRef)
}
class Person extends Actor {
import BankAccount._
import Person._
override def receive: Receive = {
case LiveTheLife(account) =>
account ! Deposit(10000)
account ! Withdraw(90000)
account ! Withdraw(500)
account ! Statement
case message => println(message.toString)
}
}
val account = system.actorOf(Props[BankAccount], "bankAccount")
val person = system.actorOf(Props[Person], "billionaire")
person ! LiveTheLife(account)
}
//result
TransactionSuccess(successfully deposited 10000)
TransactionFailure(insufficient funds)
TransactionSuccess(successfully withdrew 500)
Your balance is 9500