Udemy-akka-essential # 05 Actors, Messages and Behaviors - Exercises

Ada·2023년 10월 12일

Akka

목록 보기
27/32

앞서 배운 내용에 대한 연습 문제를 만들었다.

사실 내가 만든 건 아니고, 강사님이 만드신거지만 설명과 코드를 같이 보니 이해가 조금 되는 것 같다.

첫 번째 연습 문제에서는 내부 변수를 가진 '카운터 액터'를 생성하여 증가, 감소, 출력의 기능을 하도록 만들었다.

두 번째 연습 문제는 은행 계좌를 액터로 만드는 것이였다. 출금, 입금, 명세서 메시지를 수신하여 성공과 실패에 대한 메시지를 출력하도록 했다.
계좌는 내부 변수를 사용해 자금을 유지하였고, 입금 및 출금 금액에 따라 내부 자금을 변경하였다.

모든 메시지 전송은 항상 비동기적이고 논블로킹 이므로 여러 메시지가 서로 중첩되지 않는지,
내부 상태 변경 시 동기화 처리가 필요 없는지에 대한 의문 제기가 늘 필요하다.


아래의 코드는 어제 작성한 게시물의 코드와 이어진다.

추가적으로 정리한 내용은 코드 내부의 주석에 있다.

  /**
   * 연습 문제들
   *
   * 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
profile
백엔드 프로그래머

0개의 댓글