はじめに
前回に引き続き、Ruby学習のアウトプットとして今回は自動販売機のプログラムを作成しました。
上記の内容について、備忘のためにメモ書きを残しておくことにします。
仕様
Suicaのみで購入可能な自動販売機のプログラムとします。
Suica
- デポジットとして500円がデフォルトでチャージされているものとする
- Suicaには100円以上の任意の金額をチャージできる
- 100円未満をチャージしようとした場合は例外を発生させる
- Suicaは現在のチャージ残高を取得できる
自動販売機の在庫管理
- 自動販売機は飲み物を3種類(ペプシ、モンスター、いろはす)を格納できる
- 飲み物は名前と値段の情報をもつ
- 初期状態で、3種類の飲み物はそれぞれ5本格納されている
- 自動販売機は在庫を取得できる
名前 | 価格 |
---|---|
ペプシ | 150円 |
モンスター | 230円 |
いろはす | 120円 |
自動販売機の購入処理
- 自動販売機は3種類の飲み物が購入できるかどうかを取得できる
- 飲み物の値段以上のチャージ残高がある条件下で、購入操作を行うと以下の動作をする
- 自動販売機は、該当の飲み物の在庫を減らす
- 売り上げ金額を増やす
- Suicaのチャージ残高を減らす
- チャージ残高が足りない、もしくは該当の飲み物の在庫がない状態で購入操作を行った場合は例外を発生させる
- 自動販売機は、現在の売上金額を取得できる
制約
- Classを用いてオブジェクト指向で書く
- 3つのクラスを定義する
- クラスのインスタンスは以下の通りとする
- Suica:1枚で1インスタンス
- 飲み物:1本で1インスタンス
- クラスのインスタンスメソッド内で標準入力を受け取るコードは避ける
- 値を受け取りたい場合は、メソッドの引数などで渡す
- クラスのインスタンスメソッド内で標準出力するコードは避ける
- 標準出力に出す必要はなく、値を返すメソッドを実装する
-
attr_accessor
の利用は避ける- セッターメソッド、ゲッターメソッドを定義する
プログラムの検討
コーディングに入る前に、仕様が文字ばかりで理解がしづらいためシーケンス図に図解して整理しておくことにします。少し分かりやすくなった気がします。
それでは、コーディングしていきます。まずは、Suicaのクラスを作成します。
suica.rb
class Suica
attr_reader :deposit
def initialize
@deposit = 500
end
def charge(money)
if money < 100
puts '最低チャージ可能金額は、100円からになります。'
else
@deposit += money
end
end
def pay(money)
@deposit -= money
end
end
次に、飲み物のクラスです。
suica.rb
class Drink
attr_reader :name, :price
def initialize(name, price)
@name = name
@price = price
end
end
最後に、自動販売機のクラスです。個人的には、在庫管理と購入処理の部分の難易度が他と比較し高く感じました。
学びとしては、購入処理(buy
メソッド)内にreturn
が必要ということです。なぜなら、return
がないと以下のエラーが起きてしまいます。
※return
は、return
時点で処理を終了させることができます。
-
selected_drink
がnil
の場合(飲み物の在庫がない場合)-
NilClass
にはprice
メソッドが定義されていないため、selected_drink.price
でNoMethodError
が発生する
-
- チャージ残高が足りない場合
-
return
がないため、チャージ残高が足りない場合でも後続の購入処理が走ってしまう
-
vending_machine.rb
class VendingMachine
attr_reader :sales
def initialize
@sales = 0
@stock = []
5.times do
@stock << Drink.new('pepsi', 150)
@stock << Drink.new('monster', 230)
@stock << Drink.new('irohasu', 120)
end
end
def show_stock
puts "pepsiの在庫は、#{@stock.count { |stock| stock.name == 'pepsi' }}個です"
puts "monsterの在庫は、#{@stock.count { |stock| stock.name == 'monster' }}個です"
puts "irohasuの在庫は、#{@stock.count { |stock| stock.name == 'irohasu' }}個です"
end
def add_stock(drink)
@stock << drink
end
def buy(suica, drink)
selected_drink = @stock.find { |stock| stock.name == drink }
unless selected_drink
puts "#{drink}の在庫がありません"
return
end
if suica.deposit < selected_drink.price
puts 'チャージ残高が足りません'
return
end
@sales += selected_drink.price
@stock.delete_at(@stock.find_index { |stock| stock.name == drink })
suica.pay(selected_drink.price)
end
end
おわりに
今回、初めてオブジェクト指向プログラミングを体験しました。
自動販売機のお題は、複数のオブジェクト指向の要素が盛り込まれており難易度も適切だったかもしれません。やはり、期待する結果が出力された瞬間が楽しいです。
今後も引き続きプログラミングの学習を継続していきます。