トラック
/
Crystal
Crystal
/
演習
/
ヨハネスのジュースメーカー
ヨハネスのジュースメーカー

ヨハネスのジュースメーカー

学習演習

はじめに

クラスは、オブジェクト指向プログラミングの中心となる概念です。 Crystalでは、すべてがオブジェクトです。すべてのオブジェクトには型があり、いくつかのメソッドに応答できます。 クラスはオブジェクトを作るための設計図で、変数のような状態の初期値や、メソッドのような振る舞いの実装を提供します。 オブジェクトはクラスのインスタンスで、すべてのインスタンス変数と状態を保持します。

クラスの定義

クラスは、classキーワードを使い、そのあとにUpperCamelCaseで書いたクラス名を続けて定義します。

class Account
end

インスタンスの作成

クラスのインスタンスを作成するには、newメソッドを使ってクラス名を呼び出します。

class Account
end

account = Account.new

上の例では、変数accountはクラスAccountのインスタンスです。

インスタンスを作成するとき、initializeという特別なメソッドがコンストラクターの役割を果たします。 これはインスタンスが作成されるときに呼び出されます。 コンストラクターを使うと、インスタンスの初期状態を設定できます。 initializeメソッドは引数を受け取ることができ、その引数はインスタンスを作成するときに渡します。

class Account
  def initialize(number)
    5 + number # => 9
  end
end

Account.new(4)
Note

initializeメソッドは値を手動で返すことはできません。代わりに、newが新しく作成したインスタンスを返します。

class Account
  def initialize(number)
    5 + number
  end
end

Account.new(4)
# => #<Account:0x7f5dc33dcea0>

インスタンスメソッド

インスタンスメソッドは、クラスのインスタンスで利用できるメソッドで、そのインスタンスに対して呼び出せます。 インスタンスメソッドは、defキーワードを使い、そのあとにメソッド名を続けて定義します。 クラス定義の中に定義します。

class Account
  def balance
    100
  end
end

account = Account.new
account.balance # => 100

インスタンス変数

インスタンス変数は、そのインスタンスのすべてのメソッドで利用できる変数です。 インスタンス変数にアクセスするには、インスタンスメソッドを定義する必要があります。 インスタンス変数はinitializeメソッドで初期化できます。 @を先頭に付けて定義します。

class Account
  def initialize
    @balance = 100
  end

  def balance
    @balance
  end
end

account = Account.new
account.balance
# => 100

インスタンス変数は、クラス定義の中でも定義できます。

class Account
  @age = 0
end

初期化をともなうインスタンス変数

インスタンス変数は、initializeメソッドに渡された引数で初期化できます。 Crystalは初期化時に使われる引数の型を推論できないため、型を指定する必要があります。 これについて詳しく知りたい場合は、型推論を読んでみてください。 変数の型を指定するには、:記号のあとに型を書きます。 たとえば、Int32型のbalanceというインスタンス変数を作りたい場合は、@balance : Int32と書きます。 また、Bool型のrainingというインスタンス変数を作りたい場合は、@raining : Boolと書きます。

実装方法はいくつかあります。たとえば、initializeメソッドの引数に型を宣言し、その引数をインスタンス変数に代入する方法です。

class Account
  def initialize(balance : Int32)
    @balance = balance
  end

  def balance
    @balance
  end
end

account = Account.new(1)
account_2 = Account.new(5)

account.balance
# => 1

account_2.balance
# => 5

もう一つの方法は、クラス定義の中で@<variable> : <Type>の形でインスタンス変数の型を宣言する方法です。

class Weather

  @raining : Bool

  def initialize(raining)
    @raining = raining
  end

  def raining
    @raining
  end
end

weather = Weather.new(true)
weather.raining
# => true

インスタンス変数の変更

メソッドはインスタンス変数を変更できます。 これらのメソッドはクラスのインスタンスに対して呼び出せます。 メソッドがインスタンス変数を変更すると、その変更は呼び出したクラスのインスタンスだけに反映されます。

class Account
  def initialize(balance : Int32)
    @balance = balance
  end

  def withdraw(amount)
    @balance -= amount
  end

  def balance
    @balance
  end
end

account = Account.new(10)
account_2 = Account.new(10)
account.balance
# => 10

account_2.balance
# => 10

account.withdraw(5)
account.balance
# => 5

account_2.balance
# => 10

クラスメソッド

クラスメソッドは、クラスに対して定義されるメソッドで、そのインスタンスに対しては定義されません。 インスタンスの状態に依存しないメソッドを作る手段になります。 クラスメソッドは、defキーワードを使い、そのあとにself.<method name>を続けて定義します。 selfは、selfが呼び出されている名前空間への参照です。 ここでは、クラスAccountを指します。 Account.と書いた場合と同じになりますが、self.を使うのが好ましい書き方です。

class Account
  def self.balance
    100
  end
end

Account.balance
# => 100

説明

友人のJohannesは、DIY(自分で作ること)のハードウェアが大好きです。 Johannesはちょうどジューサーメーカーを作り終えたところで、とてもわくわくしています。 Johannesの目標は、毎朝新鮮なジュースを作れるようになることです。

Johannesのジューサーメーカーには、たくさんのすてきな機能が備わっています。 たとえば、カップにどれだけジュースが入っているかを教えてくれる、組み込みの計量システムです。 マシンがどのくらい動いているかを教えてくれるタイマーもあります。

Johannesはプログラミングが得意というわけではありませんが、このマシンを制御するプログラムを作りたいと思っています。 そこで、手伝ってほしいと頼んできました。

1. デバッグライト

マシンにはデバッグライトが付いています。仕組みは単純で、マシンに電力が供給されていればデバッグライトが点灯します。 プログラムは、デバッグライトが点灯しているときだけ実行されます。

このメソッドは、JuiceMakerという名前のクラスメソッドにします。

trueを返すクラスメソッドJuiceMaker.debug_light_on?を定義します。

JuiceMaker.debug_light_on?
#=> true

2. マシンを初期化する

マシンの電源が入ると、JuiceMakerというクラスのインスタンスが生成されるようにします。 マシンには、初期化された状態を持たせることにします。 この初期状態には、次の情報を保持させます。

  1. マシンが動作中かどうか。電源を入れたときは、常にfalseになります。
  2. カップに入っているジュースの量。これは、カップの中のジュースの量を測るセンサーになります。

カップに入っているジュースの量を引数に取る初期化処理を定義し、初期状態を組み立てます。 この初期状態には、falseに設定されたインスタンス変数@runningと、クラスの初期化時に渡された引数に基づくインスタンス変数@fluidを持たせます。

JuiceMaker.new(5)
#=> #<JuiceMaker:0x10f0b8 @running=false, @fluid=5>

3. マシンの電源を入れる

マシンは電源を入れたり切ったりできます。 マシンの電源を入れるメソッドを定義する必要があります。 マシンの電源を入れると、動作状態はtrueに設定されます。

マシンの電源を入れるメソッドJuiceMaker#startを定義します。

juice_maker = JuiceMaker.new(5)
juice_maker.start

juice_maker
#=> #<JuiceMaker:0x10f0b8 @running=true, @fluid=5>

4. マシンの状態

マシンは、自分の状態を教えてくれる必要があります。 マシンが動作中ならtrueを、動作していなければfalseを返します。

マシンの状態を返すメソッドJuiceMaker#running?を定義します。

juice_maker = JuiceMaker.new(5)
juice_maker.start

juice_maker.running?
#=> true

5. ジュースを追加する

マシンはカップにジュースを追加できます。 追加されたジュースの量は、マシンが把握できます。 ジュースを追加したあとにカップへ入っているジュースの量を知るには、マシンに手助けが必要です。

追加するジュースの量を引数に取り、カップの中のジュースの量を更新するメソッドJuiceMaker#add_fluidを定義します。

juice_maker = JuiceMaker.new(5)
juice_maker.add_fluid(5)

juice_maker
#=> #<JuiceMaker:0x10f0b8 @running=false, @fluid=10>

6. マシンの電源を切る

マシンは電源を入れたり切ったりできます。 マシンの電源を切るメソッドを定義する必要があります。 このメソッドは、マシンが動作しているときだけ呼び出されます。 マシンの電源を切ると、動作状態はfalseに設定されます。 また、電源を切ったあとにカップへ入っているジュースの量を求めるのにも、マシンには手助けが必要です。 マシンは1分あたり5単位のジュースを消費します。

マシンが動作していた分数を引数に取り、電源を切ったあとのカップの中のジュースの量を返すメソッドJuiceMaker#stopを定義します。

juice_maker = JuiceMaker.new(5)

juice_maker.start
juice_maker
#=> <JuiceMaker:0x10f0b8 @running=true, @fluid=5>

juice_maker.stop(1)
#=> #<JuiceMaker:0x10f0b8 @running=false, @fluid=0>
GitHubで編集する リンクは新しいウィンドウまたはタブで開きます
Crystal Exercism

ヨハネスのジュースメーカーを始める準備はできましたか?

Exercismに登録すれば、26個のコンセプト142個の演習、そして本物の人間によるメンタリングとともに、Crystalを学んでマスターできます。すべて無料です。