ポケモンでわかるかもしれないRubyのクラス
これはTama.rbでLTした内容をリバイスしたものです。
Tama.rbでは現在「プロを目指すためのRuby入門」の輪読会を行なっております。
「第7章クラスの作成を理解する」の章を輪読した際、内容理解の手助けになればと思い、作成したものになります。
この記事のゴール
以下についてざっくり理解できる(かもしれません)。
- オブジェクト指向プログラミングの基礎
- クラスの定義
- インスタンスメソッドの定義
- 定数
- クラスの継承
行うこと
ポケモンを用いて、7章で説明されている概念を例えで理解できるようにしたい。
今回はオブジェクト指向を使って、ある一匹のポケモンを作ってみようと思います。
今回作成するポケモン
今回作るのは…ピカチュウ、君に決めた!!
要件の確認
「ある一匹のピカチュウ」を表現するには、どうすれば良いでしょうか。
まず、今回作るピカチュウの要件を決めるために、ピカチュウの特徴を列挙してみます。
・ポケモンの一種
・複数存在する(サトシのピカチュウ、やせいのピカチュウ)
・わざとか使える
・ステータスとか確認できる
「ポケモンの一種である」、「複数存在する」といった条件をまとめると
下記のようになりそうです。
- ポケモンという種族がいる
- その中にピカチュウという種族がいる
- ピカチュウという種族に所属する生物がたくさんいる
クラスを実装する
上記の要件を実際コードに落とし込んでみると、下記のように書けます。
class Pokemon # ポケモンクラス
end
class Pikachu < Pokemon # ピカチュウクラス
end
pikachu = Pikachu.new #ピカチュウクラスから一匹のピカチュウをnewする
こうした継承関係を作ることで、ポケモンクラスを拡張する形でピカチュウクラスを作成することが出来ます。
口頭で確認する
念の為、正しく継承関係が作れているか確かめてみましょう。
本の7章本文には
「サブクラスはスーパークラスの一種である(サブクラス is a スーパークラス)」と口に出して違和感がないか確かめてみる
という方法が書いてあります。
「ピカチュウはポケモンの一種である」(is-aの関係)と読み替えても違和感なさそうですね。
コードで確認する
念の為コードで確認してみます。
class Pokemon #ポケモンクラス
end
class Pikachu < Pokemon #ピカチュウクラス
end
pikachu = Pikachu.new
puts pikachu.class #=>Pikachu
puts pikachu.class.superclass #=>Pokemon
1匹のピカチュウ(インスタンス)のクラスはPikachu
PikachuクラスのスーパークラスはPokemonとなっており
要件を満たす形で継承関係が作れていますね。
それぞれのクラスの役割分担
それぞれのクラスはどういった役割を担っているでしょうか。
文字に落とし込んでみると、下記のようなことが言えます。
- Pokemonクラスは「ポケモン」という種族の設計図
- Pikachuクラスはポケモン図鑑等で確認できる、「ピカチュウ」という種族全般の設計図
- Pikachuクラスからnewされたインスタンスは、実際バトルに使える1匹のピカチュウ
各クラスの位置付けが確認出来た所で、次にメソッドを実装していきましょう。
クラスにメソッドを実装する
メソッドは「振る舞い」とも呼ばれます。各クラスにメソッドを実装することで、期待する振る舞いを持たせていきましょう。
まず先ほどと同様、各クラスに求める要件を羅列していきます。
class Pokemon # ポケモンクラス
# どのポケモンでも共通するような要素の雛形を持つ
# HP、こうげき、ぼうぎょ、すばやさなどのステータスを保持
# ポケモンにステータスを尋ねると、自分のステータスを教えてくれる
end
class Pikachu < Pokemon # ピカチュウクラス
# すべてのピカチュウに共通する要素全般
# 図鑑に乗っているようなデータ(体重、身長、特徴)を確認したい
# 個々のピカチュウ
# ステータスを確認したい
# わざを確認したい・使いたい
# ニックネームをつけたい
end
# Pikachu.newしたインスタンス
# それぞれ違ったステータスのパラメータを持っている
上記にしたがって、各クラスにメソッドを追加していきましょう
Pokemonクラスへのメソッド実装
ポケモンクラスはすべてのポケモンの設計図にあたり
このクラスから様々なポケモンが生成できます。
どのポケモンでも共通するような要素の雛形を持っていて欲しい
とあるので、早速要件に沿った実装をしていきます。
class Pokemon
attr_reader :name, :level, :hp, :attack, :diffence, :speed, :sp
def initialize(name:, level:, hp:, attack:, diffence:, speed:, sp:)
@name = name
@level = level
@hp = hp
@attack = attack
@diffence = diffence
@speed = speed
@sp = sp
end
def show_status
"なまえ:#{name} レベル:#{ level } HP:#{ hp } こうげき:#{ attack } ぼうぎょ:#{ diffence } すばやさ:#{ speed } とくしゅ:#{ sp }"
end
end
これでステータスの雛形が出来ました。
Pokemonクラスをクラスを作成した際、クラスの引数に各値を渡してあげることでステータスを保持できるようになります。
また、statusというメソッドを使うことでそのポケモンのステータスを確認することが出来ます。
Pikachuクラスのメソッド実装
次はPikachuクラスの実装をしていきましょう。
ピカチュウ種族に共通する要素
公式によると、どのピカチュウも例外なく同じ高さを重さを持っています。(まじか)
また、ピカチュウはある決まった「わざ」を持っています。
たいあたりとか、でんきショックがこれにあたりますね。
早速実装してみましょう
種族データ
まずは種族データからです。
ポケモン図鑑で確認できるアレのことですね。
CLASSIFIED_DATA = { classification: "ねずみポケモン", hight: 0.4, weight: 6.0 }
def show_classified_data
CLASSIFIED_DATA
end
この値は変化しないことが保証されているので、CLASSIFIED_DATAという定数にピカチュウのデータを持たせます。
わざの実装
次に覚えているわざを実装していきましょう。
def cry
"#{ name }のなきごえ"
end
def thundershock
"#{ name }のでんきショック"
end
これでPikachuクラスのインスタンスからこれらを呼び出すことで、わざを使えたり、種族データを教えてくれるようになりました。
まとめると下記のようになります。
class Pikachu < Pokemon # ピカチュウクラス
CLASSIFIED_DATA = { classification: "ねずみポケモン", hight: 0.4, weight: 6.0 }
def show_classified_data
CLASSIFIED_DATA
end
def cry
"#{ name }のなきごえ"
end
def thundershock
"#{ name }のでんきショック"
end
end
ピカチュウクラスのインスタンス
最後に、個々のピカチュウについて検討していきましょう。
ピカチュウは複数存在し、バラバラのステータスを持っています。
ここではピカチュウのレベル5として、それぞれのステータスの下限と上限を設定します。
公式によると、下記のようなステータスのばらつきがあるようです。
項目 | HP | こうげき | ぼうぎょ | とくこう | とくぼう | すばやさ | とくしゅ |
---|---|---|---|---|---|---|---|
最大値 | 23 | 15 | 12 | 14 | 13 | 18 | 14 |
最小値 | 18 | 10 | 8 | 10 | 9 | 14 | 10 |
ここではrandメソッドを使って、それぞれのステータスの値がランダムで決定するようにします。
初期値を渡してあげましょう。
pikachu = Pikachu.new(name: "ピカチュウ")
これで作成できたはずです。
ここまでのコードを一度確認し、ピカチュウのステータスが作成できているか確認してみましょう。
class Pokemon
attr_reader :name, :level, :hp, :attack, :diffence, :speed, :sp
def initialize(name)
@name = name
@level = 5
@hp = rand(18..23)
@attack = rand(10..15)
@diffence = rand(8..12)
@speed = rand(10..14)
@sp = rand(10..14)
end
def show_status
"なまえ:#{name} レベル:#{ level } HP:#{ hp } こうげき:#{ attack } ぼうぎょ:#{ diffence } すばやさ:#{ speed } とくしゅ:#{ sp }"
end
end
class Pikachu < Pokemon
CLASSIFIED_DATA = { classification: "ねずみポケモン", hight: 0.4, weight: 6.0 }
def show_classified_data
CLASSIFIED_DATA
end
def cry
"#{ name }のなきごえ"
end
def thundershock
"#{ name }のでんきショック"
end
end
pikachu = Pikachu.new(name: "Tama太郎")
puts pikachu
#=>#<Pikachu:0x000055fa98dae448>
puts pikachu.show_classified_data
#=>{:classification=>"ねずみポケモン", :hight=>0.4, :weight=>6.0}
puts pikachu.show_status
#=>
# なまえ:Tama太郎
# レベル:5
# HP:20
# こうげき:10
# ぼうぎょ:8
# すばやさ:13
# とくしゅ:14
無事ピカチュウが作成出来ました!
追加要素など
Tama.rb の会ではその他
- 選別のためステータスが一定以下ならポケモンセンター送りにする
- わざましん module を include することで新しい「わざ」を覚えさせる
- ニックネームをつけてあげる
等々の説明を行いましたが、クリスマス準備で力尽きたのでこのあたりで…。
もし余力があれば続きは「発展編」で書いてみようと思います。
あとがき
最初にクラスの説明に触れたときのことです。「たいやき」や「車」の例えを読んで、「たいやきのメソッドってなんやねん」やら、「車はそもそも詳しくないから興味湧かない…」ということを思っておりました。
もしそれらの例えがしっくりこないんだよなーという方がいらっしゃったら、自分の好きなゲームで例えて実装するのはおすすめかもしれません。ゲームのキャラクターは実社会の仕組みよりも体系化されていて、例外も少ないことから「値を持たせる」「継承する」「メソッドを使う」等々の仕組みが、より表現しやすいように感じます。
そして何より興味が持てるので「手を動かして実装してみよう!」というモチベーションが沸きますから。