はじめに
先日、こちらのQiita記事を拝見しました。
この記事では「たい焼きの型」をクラスと見なして、クラスとオブジェクトの違いを説明しています。
ただ、着眼点は良かったのですが、記事の中で使われているサンプルコードが少しオブジェクト指向プログラミングの面白さを伝え切れていないように感じました。
そこでこの記事では「たい焼き」をお題にして、オブジェクト指向プログラミングの考え方(というか、面白さ)を簡単に説明していきます。
動画がメインです!
この記事の内容はYouTubeで解説しています。
というか、動画の方がメインで、この記事はオマケです。
ぜひ最初に動画の方をチェックしてください!
【初心者向け】たい焼きクラスで学ぶ、楽しいオブジェクト指向プログラミング入門 - YouTube
ちなみに、動画の長さは30分ちょっとあるので、1.5倍速ぐらいで見ることをオススメします。
コードはGitHubに置いています
ソースコードはGitHubに置いてあるので、必要に応じてこちらも参照してください。
JunichiIto/taiyaki-sandbox: Sample app for object oriented programming
Minitestで動作確認しています
動画を見てもらうとわかると思いますが、作成したコードはMinitestを使って動作確認しています。
あまり難しいことはやっていないと思いますが、何をやってるのかようわからん、という方はこちらの記事を参考にしてみてください。
(test-unitの話がメインですが、記事の後半にMinitestの話も出てきます)
Ruby標準のテスティングフレームワークで手軽にテストコードを書く方法 - Qiita
本物のたい焼きをイメージしながらコードを書いていきます
動画の中ではこちらのWebサイトを見ながら、「本物のたい焼き」をイメージしています。
(ただし、このサイトの情報どおりに実装しているわけではありません)
「本物のたい焼きはどんな状態を持つか?」ということを考えながらTaiyakiクラスを実装していくと、よりいっそう楽しくオブジェクト指向プログラミングができるできるはずです!
Step 0. Taiyakiクラスを定義する
何はともあれ、Taiyakiクラスを定義してあげましょう。
中身はこれから実装していきます。
class Taiyaki
end
Step 1. 「あんこ」の種類を保持できるようにする
Taiyakiクラスにインスタンス変数の@anko
を追加し、「あんこ」の種類を保持できるようにします。
ここでは以下の2種類を想定します。
- あずき
- 白あん
require 'minitest/autorun'
class Taiyaki
attr_reader :anko
def initialize(anko)
@anko = anko
end
end
class TaiyakiTest < Minitest::Test
def test_taiyaki
taiyaki_1 = Taiyaki.new('あずき')
assert_equal 'あずき', taiyaki_1.anko
taiyaki_2 = Taiyaki.new('白あん')
assert_equal '白あん', taiyaki_2.anko
end
end
Step 2. 「大きさ」を保持できるようにする
Taiyakiクラスにインスタンス変数の@size
を追加し、「大きさ」を保持できるようにします。
ここでは以下の2種類を想定します。
- ふつう
- 大きめ
require 'minitest/autorun'
class Taiyaki
attr_reader :anko, :size
def initialize(anko, size)
@anko = anko
@size = size
end
end
class TaiyakiTest < Minitest::Test
def test_taiyaki
taiyaki_1 = Taiyaki.new('あずき', 'ふつう')
assert_equal 'あずき', taiyaki_1.anko
assert_equal 'ふつう', taiyaki_1.size
taiyaki_2 = Taiyaki.new('白あん', '大きめ')
assert_equal '白あん', taiyaki_2.anko
assert_equal '大きめ', taiyaki_2.size
end
end
Step 3. 自分の状態を文字列として返せるようにする
たい焼きに保持されているあんことサイズを文字列として返せるように、to_s
メソッドを実装(オーバーライド)します。
require 'minitest/autorun'
class Taiyaki
attr_reader :anko, :size
def initialize(anko, size)
@anko = anko
@size = size
end
def to_s
"あんこ: #{anko}, 大きさ: #{size}"
end
end
class TaiyakiTest < Minitest::Test
def test_taiyaki
taiyaki_1 = Taiyaki.new('あずき', 'ふつう')
assert_equal 'あずき', taiyaki_1.anko
assert_equal 'ふつう', taiyaki_1.size
assert_equal 'あんこ: あずき, 大きさ: ふつう', taiyaki_1.to_s
taiyaki_2 = Taiyaki.new('白あん', '大きめ')
assert_equal '白あん', taiyaki_2.anko
assert_equal '大きめ', taiyaki_2.size
assert_equal 'あんこ: 白あん, 大きさ: 大きめ', taiyaki_2.to_s
end
end
Step 4. 「値段」を計算できるようにする
ここでは以下のルールで値段を計算します。
- ベースとなる値段は100円
- 白あんを選ぶと30円プラス
- 大きめを選ぶと50円プラス
上のルールに従って値段を返せるように、Taiyakiクラスにprice
メソッドを追加します。
また、to_s
メソッドにも値段を出力するようにします。
require 'minitest/autorun'
class Taiyaki
attr_reader :anko, :size
def initialize(anko, size)
@anko = anko
@size = size
end
def price
amount = 100
if anko == '白あん'
amount += 30
end
if size == '大きめ'
amount += 50
end
amount
end
def to_s
"あんこ: #{anko}, 大きさ: #{size}, #{price}円"
end
end
class TaiyakiTest < Minitest::Test
def test_taiyaki
taiyaki_1 = Taiyaki.new('あずき', 'ふつう')
assert_equal 'あずき', taiyaki_1.anko
assert_equal 'ふつう', taiyaki_1.size
assert_equal 100, taiyaki_1.price
assert_equal 'あんこ: あずき, 大きさ: ふつう, 100円', taiyaki_1.to_s
taiyaki_2 = Taiyaki.new('白あん', '大きめ')
assert_equal '白あん', taiyaki_2.anko
assert_equal '大きめ', taiyaki_2.size
assert_equal 180, taiyaki_2.price
assert_equal 'あんこ: 白あん, 大きさ: 大きめ, 180円', taiyaki_2.to_s
taiyaki_3 = Taiyaki.new('あずき', '大きめ')
assert_equal 150, taiyaki_3.price
end
end
Step 5. 「賞味期限」を管理できるようにする
ここでは製造日の3日後を賞味期限に設定します。
これに伴い、以下のインスタンスメソッドを追加します。
-
produced_on
製造日(システム日付)を返す -
expire_on
賞味期限(製造日の3日後)を返す -
expired?
賞味期限が過ぎていればtrue
、そうでなければfalse
require 'minitest/autorun'
require 'date'
class Taiyaki
attr_reader :anko, :size, :produced_on
def initialize(anko, size)
@anko = anko
@size = size
@produced_on = Date.today
end
# 省略
def expire_on
produced_on + 3
end
def expired?(today = Date.today)
expire_on < today
end
# 省略
end
class TaiyakiTest < Minitest::Test
# 省略
def test_expired
taiyaki = Taiyaki.new('あずき', 'ふつう')
# 製造日 = システム日付
assert_equal Date.today, taiyaki.produced_on
# 賞味期限 = 製造日 + 3日
assert_equal (Date.today + 3), taiyaki.expire_on
# 当日なら食べられる
refute taiyaki.expired?
# 3日目まで食べられる
refute taiyaki.expired?(Date.today + 3)
# 4日目を過ぎると食べられない
assert taiyaki.expired?(Date.today + 4)
end
end
まだまだ「たい焼きクラス」で遊べそうな気がしますが、この例題はこれでおしまいです。
まとめ
というわけで、この記事では「たい焼きクラス」を使ってオブジェクト指向プログラミングの考え方を簡単に説明してみました。
このように、オブジェクト指向プログラミングではクラスを定義し、その中にデータ(インスタンス変数)とメソッドを持たせることで、実在する物(オブジェクト)に近い形でプログラミングをすることができます。
とりあえず、この記事ではあまり難しいことを言うつもりはありません。
今までクラスやオブジェクトと言われてもピンと来なかった人が、この記事(と動画)を見て、「へ~、オブジェクト指向プログラミングってなんか面白い」と思ってもらえればそれでOKです。
詳しい話は後述する参考文献などで勉強してみてください。
参考文献
オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方
オブジェクト指向設計やオブジェクト指向プログラミングを本格的に学びたい!というRubyプログラマの人にはこちらの本がオススメです。
僕(伊藤淳一)が現在制作中のRubyの入門書です。
この本の中でもRubyにおけるオブジェクト指向プログラミングの考え方をあれこれ説明しています。
オブジェクト指向関連書籍リスト - give IT a try
僕がその昔読んだオブジェクト指向関連の書籍一覧です。
ただ、記事を書いたのがかなり前なので、絶版になっている本も多いかもしれません。