트랙
/
Crystal
Crystal
/
연습 문제
/
벨브룩 바스켓 리그
벨브룩 바스켓 리그

벨브룩 바스켓 리그

학습 연습 문제

소개

Crystal의 모듈은 두 가지 목적으로 사용해요.

첫 번째 목적은 이름 충돌을 피하기 위해 네임스페이스를 만드는 거예요. 또한 코드를 묶어 주기 때문에, 그 코드가 무엇을 위한 것인지 더 쉽게 이해할 수 있게 해줘요.

두 번째 목적은 타입끼리 코드를 공유하기 위한 믹스인을 정의하는 거예요.

모듈은 클래스와 비슷하지만, 가장 큰 차이는 인스턴스를 만들 수 없다는 점이에요. 그래서 인스턴스 변수도 없어요.

모듈을 선언하려면 module 키워드 뒤에 모듈 이름을 써요.

module Foo
end

네임스페이스

네임스페이스는 코드를 묶는 방법이에요. 이름 충돌을 피하고, 그 코드가 무엇을 위한 것인지 더 쉽게 이해할 수 있게 해줘요. 네임스페이스 안에 넣어 둔 상수나 클래스에 접근하고 싶을 때는 :: 연산자를 사용해요.

module Foo
  class Bar
  end
end

Foo::Bar.new

믹스인으로 사용하기

예를 들어 여러 클래스가 같은 "기본" 기능을 필요로 하거나, 서로 관련 없는 클래스끼리 코드를 공유해야 할 때 유용해요. 관련 없는 클래스 사이에서 코드를 공유하고 싶을 때도 마찬가지예요.

모듈을 믹스인으로 사용하는 방법에는 두 가지가 있어요. 첫 번째는 include 키워드를 사용하는 것이고, 두 번째는 extend 키워드를 사용하는 것이에요.

두 방법 모두 모듈을 include하거나 extend한 타입에서 상수를 사용할 수 있게 해줘요.

include

include를 사용하면 모듈의 모든 메서드를, 모듈을 include한 타입의 인스턴스 메서드로 사용할 수 있어요. include 키워드는 타입의 맨 위에 쓰고, 뒤에 모듈 이름을 적어요.

module Foo
  def foo
    "foo"
  end
end

class Bar
  include Foo
end

Bar.new.foo # => "foo"

extend

extend는 include와 비슷하게 동작하지만, 메서드를 인스턴스 메서드로 사용할 수 있게 하는 대신 클래스 메서드로 사용할 수 있게 해요. extend 키워드도 타입의 맨 위에 쓰고, 뒤에 모듈 이름을 적어요.

module Foo
  def foo
    "foo"
  end
end

class Bar
  extend Foo
end

Bar.foo # => "foo"

extend self

Crystal에서 흔히 쓰는 패턴 중 하나는 모듈에서 extend self 패턴을 사용하는 거예요. 이렇게 하면 모듈의 모든 메서드를 모듈 자체의 클래스 메서드로 사용할 수 있어요. 즉, def self.method_name 문법으로 각 메서드를 모듈에 일일이 할당할 필요가 없어요.

module Foo
  extend self

  def foo
    "foo"
  end
end

Foo.foo # => "foo"

지침

Bellebrook Basketball League에는 여러 팀이 있고, 각 팀에는 선수 명단이 있어요. 새 시즌이 곧 시작되는데, 경기 티켓을 팔기 위한 새로운 티켓 판매 시스템을 만들어야 해요. 지난 시즌에는 경기장 입구에서 티켓을 팔았지만, 인기가 폭발적으로 높아지면서 늘어난 수요를 감당하기 위해 이번 시즌에는 온라인으로 티켓을 팔기로 했어요.

리그 측에서 연락을 해서 티켓 판매를 처리할 시스템을 만들어 달라고 부탁했어요.

1. 티켓 판매 시스템 만들기

먼저 티켓 판매를 처리할 티켓 판매 시스템을 만들어야 해요. 티켓 판매 시스템은 경기마다 티켓이 몇 장 남아 있는지 알려줄 수 있어야 해요.

TicketSystem이라는 클래스의 인스턴스를 경기마다 하나씩 만들어야 해요. 초기화된 상태에는 다음 정보가 담겨 있어야 해요:

  1. 경기에 사용할 수 있는 티켓이 몇 장인지.
  2. 경기가 어느 경기장에서 열리는지.

사용 가능한 티켓 수와 경기가 열리는 경기장을 나타내는 인자로 초기화된 상태를 만들어요. 초기화된 상태는 인스턴스 변수 @tickets_available과 @stadium으로 구성돼요. 각각 첫 번째 인자와 두 번째 인자를 담아야 해요.

TicketSystem.new(100, "Bellebrook")
# => #<TicketSystem:0x10f0b8 @tickets_available=100, @stadium="Bellebrook">

2. 사용 가능한 티켓 수 구하기

먼저, 주어진 경기에 사용할 수 있는 티켓 수를 반환하는 메서드를 만들어야 해요.

이미 정의한 클래스 위에 TicketingReservation이라는 모듈을 정의해요. 이 모듈은 TicketSystem 클래스에 포함돼야 해요.

모듈 안에 tickets_available이라는 메서드를 정의해요. 이 메서드는 사용 가능한 티켓 수를 반환해야 해요.

ticket_system = TicketSystem.new(100)
ticket_system.tickets_available
#=> 100

3. 티켓 주문하기

주어진 경기에 티켓이 남아 있는지 확인하는 메서드를 만들어야 해요. 경기에는 100장의 티켓이 예비로 남아 있어야 해요. 이렇게 하면 경기장 입구에서 티켓을 구매하는 사람들을 위해 항상 티켓이 남아 있게 돼요.

TicketingReservation 모듈 안에 order_ticket?이라는 메서드를 정의해요. 이 메서드는 티켓이 100장 이상 남아 있으면 true를, 그렇지 않으면 false를 반환해야 해요. 티켓이 100장 이상 남아 있으면, 사용 가능한 티켓 수를 1장 줄여야 해요.

ticket_system = TicketSystem.new(100)
ticket_system.order_ticket?
#=> true

4. 주문 메시지

티켓 판매 시스템에는 사용자가 쉽게 이해할 수 있는 메시지가 있어야 해요. 그리고 메시지는 개인적인 느낌이 나야 하므로, 사용자의 이름을 담아야 해요.

티켓 구매가 성공하면, 사용자는 구매가 성공했다는 것, 자신의 티켓 번호, 그리고 경기가 열리는 경기장을 알려주는 메시지를 받아야 해요. 티켓 번호는 주문 전에 사용 가능했던 티켓 수여야 해요.

구매가 성공하면 메시지는 다음과 같아야 해요: {name}, your purchase was successful, your ticket number is #{ticket_number}, and the game is played at the {stadium_name} stadium.. 구매가 실패하면 메시지는 다음과 같아야 해요: {name}, your purchase was unsuccessful, there are not enough tickets available..

TicketingReservation, 모듈 안에 구매자의 이름을 담은 인자 name을 받는 order_message라는 메서드를 정의해요. 이 메서드는 메시지가 담긴 문자열을 반환해야 해요.

ticket_system = TicketSystem.new(100, "Bellebrook")
ticket_system.order_message("John")
#=> "John, your purchase was successful, your ticket number is #100, and the game is played at the Bellebrook stadium."
GitHub에서 편집 링크가 새 창이나 탭에서 열려요
Crystal Exercism

벨브룩 바스켓 리그 문제를 시작해 볼 준비가 됐나요?

Exercism에 가입하고 Crystal 트랙을 개념 26개연습 문제 142개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.