こんにちは!侍ブログ編集部(@samuraijuku)です。
「オブジェクト指向」ってよく聞くけれどよく分からない!という方も多いかと思います。
今回侍エンジニアOnlineのイベント「ゲームで学ぶオブジェクト指向!初心者からRubyを使って簡単にマスターする3時間」では、プログラミングを学習する人なら誰でも憧れる「ゲーム作り」で楽しんでオブジェクト指向を学んで頂けるようにセミナーを開催しました!
プログラミングを学習していく中で「オブジェクト指向」という考え方はとても大切になってきます。
実はRubyやPHP、Javascriptなどを様々な有名なプログラミング言語も、オブジェクト指向の言語です。
・オブジェクト指向とは一体なんなのか?
・オブジェクト指向だと何が良いのか?
・もっとプログラミングを概念的に理解したい!
そんな疑問に対して、今回はRPGゲームの機能実装を行い、その有用性を体感していただきました。
プログラミングを学ぶ上で、そしてエンジニアとして仕事していく上で、知っておいて損はない言葉ですので、レポートを読みながら「オブジェクト指向」について理解も深めていただければと思います。
それでは参りましょう!
オブジェクト指向の基礎を理解しよう!
このイベントの趣旨
WEBアプリを開発する際に使用する、多くのプログラミング言語は「オブジェクト指向」の言語です。
例えば、「Ruby」「PHP」「Javascript」といった言語もオブジェクト指向なのですが、オブジェクト指向の使い方やメリットをしっかりと理解しているプログラミング学習者はあまり多くありません。
オブジェクト指向をイチから理解することで、
・機能追加をしやすい設計ができるようになる
・手間を省けるようになる(開発の時間を減らせる)
・綺麗なコードが書けるようになる(誰が見てもわかりやすい)
このようなメリットがあります。
「普通のエンジニア」と「できるエンジニア」の差は、このオブジェクトの概念の理解度がとても大きなものになります。
今回のイベントでは、ゲーム機能の実装をワークにした実践的な内容で「オブジェクト指向の概念と構造」を体感してもらいました。
オブジェクト指向とは?
まずはじめに、オブジェクト指向とは何なのか?について、インストラクターの井上が具体的に説明していきます。
重要な「オブジェクト」「クラス」「インスタンス」「カプセル化」「継承」というキーワードを1つずつ解説しながら、簡単なワークにより実際にRubyのソースコードを実行して理解をしていきます。
言語により多少特徴が違いますが、オブジェクト指向の考え方は基本的に同じようなものなので、1つの言語で習得できると、他のオブジェクト指向言語の習得も簡単になります。
例えば、Rubyを学んだ人がPHPを学習した際、1ヶ月もしないで仕事ができるレベルまで理解することも可能です。
まずは前提知識となる概念の基礎をきっちり抑えていきます。
オブジェクト指向の考え方
オブジェクト指向が分かれば効率良くアプリができる
- 拡張性の高いソース
- 読みやすいソース
- バグが出にくいソース
カプセル化
オブジェクト指向で基本となる概念は「カプセル化」です。
これは、オブジェクトに属する値や操作はすべてオブジェクト内部に埋め込んでカプセル化してしまおう、という概念です。
「カプセル化」すると、「コードが変更について閉じていて、拡張について開いている」ということが可能になります。
つまり、コード開発後の修正によるコード変更をしないでよく、コード開発後にプログラムを発展させて拡張していくことが可能になります。
まずは、カプセル化していないコードを見ていきましょう。
class Animal def initialize(animalname="animal") @name = animalname end attr_accessor :name end animal = Animal.new aanimal.name = "dog" + 1.to_s puts animal.name animal.name = "cat" + :dog.to_s puts animal.name
実行結果:
dog1 catdog
animal.nameはカプセル化していないため、クラス(オブジェクト)内部で「= “dog”」や「+1.to_s」していません。
そのため、animal.nameに対する操作は、コードのあちらこちらに散在することになります。
このため、animal.nameの取り扱いを変更することになった場合、コードのあちらこちらに散在するanimal.nameをすべて変更しないといけないことになります。
続いて、カプセル化したコードを見ていきましょう。
class Animal def initialize(animalname="animal") @name = animalname end attr_reader :name def add_option_name(optionName) @name += optionName.to_s end end animal = Animal.new # カプセル化し、to_sメソッドを不要に animal.add_option_name(1) puts animal.name # カプセル化し、to_sメソッドを不要に animal.add_option_name(:dog) puts animal.name
実行結果:
animal1 animal1dog
上のサンプルコードでは、animal.nameに関する変更がクラス(オブジェクト)の内部の「add_option_name」メソッドに集約されています。
これで、animal.nameに関する操作を変更したい場合は、「add_option_name」のみ変更すれば良いことになります。
実際に、「add_option_name」を変更したサンプルコードを見てみましょう。
class Animal def initialize(animalname="animal") @name = animalname end attr_reader :name def add_option_name(optionName) # 変更点する際は、クラス内部のメソッド一つ内のみ変更すれば良い @name += "【" + optionName.to_s + "】" end end animal = Animal.new animal.add_option_name(1) puts animal.name animal.add_option_name(:dog.to_s) puts animal.name
実行結果:
animal【1】 animal【1】【dog】
このように、変更点は「add_option_name」メソッド内部のコードのみであり、メインのコードの変更はありません。
このことは、開発するコードの保守性が優れていることになります。
一方、カプセル化しなていないと、コード変更時に、以下のようなサンプルコードになってしまいます。
class Animal def initialize(animalname="animal") @name = animalname end attr_accessor :name end animal = Animal.new # クラス外部で変数操作 animal.name += "【" + 1.to_s + "】" puts animal.name # クラス外部で変数操作2回目 animal.name += "【" + "dog" + "】" puts animal.name
実行結果:
animal【1】 animal【1】【dog】
カプセル化していないと、メインのコードに散在するanimal.nameに関する[r:全ての操作を変更しないといけなくなります。
これでは、バグも多発し、テストも大変です。
カプセル化は以上のようなメリットがあります。
拡張(継承)
オブジェクト指向は「コードの変更について閉じていて、拡張について開いている」のでした。
それでは、コードに機能を追加したいときは、どのようにするのでしょうか?
答えは「クラス(オブジェクト)を拡張(継承)する」です。
以下、親クラスAnimalに対し、親クラスを拡張(継承)した子クラスHumanについてのサンプルコードを見ていきましょう。
class Animal def initialize(animalname="animal") @name = animalname end attr_reader :name def add_option_name(optionName) # 変更点する際は、クラス内部のメソッド一つ内のみ変更すれば良い @name += "." + optionName.to_s end end class Human < Animal # 親クラスを拡張して新しいメソッドを作る def call puts "Hello, Mr." + @name end end human = Human.new("Steave") human.add_option_name("Jobs") human.call
実行結果:
Hello, Mr.Steave.Jobs
親クラスにはなかったメソッド「call」を子クラスで追加しています。
そのため、メインのコードで変数humanに対し、callメソッドを呼ぶことができています。
これは「call」という機能を拡張(継承)によって実現した、ということです。
ポリモルフィズム
オブジェクト指向ではさらに進んだ概念として「ポリモルフィズム(多様性)」という概念があります。
たとえば、親クラスにcallメソッドがあり、子クラスにもcallメソッドがあった場合を考えます。
親クラスではcallメソッドにより「Hello!」と表示します。
子クラスではcallメソッドにより「Good morning!」と表示します。
このように、メインのコードからは同じコールメソッドを呼びますが、親クラス、子クラスのどちらのクラスのオブジェクトを扱っているかで動作が変わります。
メインのコードは、常にcallメソッドさえ使っていればよく、メインのコードは変更する必要がありません。
このように「ポリモルフィズム(多様性)」により、クラス(オブジェクト)の動作を変えて、機能の変更を行うことができます。
実際のサンプルコードを見ていきましょう。
class Animal def initialize(animalname="animal") @name = animalname end attr_reader :name def add_option_name(option_name) # 変更点す際は、クラス内部のメソッド一つ内のみ変更すれば良い @name += "." + option_name.to_s end end class Human < Animal # 親クラスを拡張して新しいメソッドを作る def call puts "Hello, Mr." + @name end # 親クラスのメソッドを上書きして、ポリモルフィズムを行う def add_option_name(option_name) # 変更点す際は、クラス内部のメソッド一つ内のみ変更すれば良い @name += "." + option_name.to_s + " called Great Guy!" end end human = Human.new("Steave") human.add_option_name("Jobs") human.call
実行結果:
Hello, Mr.Steave.Jobs called Great Guy!
上のサンプルコードでは、子クラスを変数化(インスタンス化)しています。
そして、子クラスのhuman変数に対し、「add_option_name」メソッドを呼んでいます。
この「add_option_name」メソッドは、ポリモルフィズムにより、親クラスとは違う子クラス独自のコードになっています。
もし、human変数に親クラスが入っていた場合は、親クラスの「add_option_name」メソッド」が呼ばれます。
最後に「call」メソッドにより画面にデータを出力しています。
このように、拡張したクラスで親クラスのメソッドの動作を変更することができます。
この変更を行っても、メインのコードは変わりません。「add_option_name」メソッドを呼ぶコードのままです。
このように、
- カプセル化によるデータと操作の隠蔽
- 拡張(継承)による機能の追加
- ポリモルフィズム(多様性)による機能の変更
のオブジェクト指向の基本を実施すると、コードの保守性を犠牲にすることなく、コードに機能を追加できます。
なお、オブジェクト指向について具体的に理解したい方は、こちらもご覧ください。
オブジェクト指向をワークを通してきちんと理解
今回のセミナーでは、侍エンジニアで学習を推奨している「Ruby」を使ってワークを進めていきました。
Rubyは直感的に読みやすくJava等と比較しても分かりやすい言語になっています。
特にオブジェクト指向の基本的な考え方として、親クラスの特徴を引き継ぐ継承や、データを適切な処理でのみ変更するカプセル化など重要な概念を抑えていきます。
実際にオブジェクト指向のサンプルプログラムを解析&改造
事前に用意されていたサンプルソースのファイルを用いて、実際に動作をさせて文字を出力していきます。
普段、あまり気にせず文字を出力するコードを書いたりしますが、簡単なコードでも概念を理解しながら進めることで、ふとした気づきがあったりします。
その気づきこそが、よりラクにコードを書く”コツ”でもあります。みなさん、新しい発見をしながらワークを進めてきました。
Macのターミナルを使ったり、Cloud9使ったり、みなさんそれぞれのRuby環境の中でワークを行っています。
環境構築がまだの人には、インストラクターがレクチャーしながらワークができる環境を作っていきました。
ゲームの機能をワーク形式で実装!
RPG GAME MISSION
- 【初級】ゲーム開始時に勇者の名前を入力してもらい、任意に変えられるようにしてみましょう。
- 【初級】やられた時の叫び声を勇者と敵でそれそれ別の、オリジナルのものに変更してください。
- 【初級】通常攻撃時に確率でダメージが増加する”クリティカルヒット”を実装してください。
- 【初級】攻撃時に2秒処理を止めてみましょう。また攻撃時に攻撃を表現するアスキーアート(絵文字)を入れてみましょう。
- 【中級】コマンド入力で「通常攻撃」か、「必殺技」かを選べるようにしてください。
- 【中級】HPや攻撃力などのパラメータの概念を取り入れましょう。
- 【中級】対戦できる敵を選べるようにしましょう。
- 【上級】ドラクエ風にパーティで複数人対複数人で対戦できるようにしましょう。
さて、一通りのキーワードを学んだところで、ゲーム開発ミッションにチャレンジします!
それぞれのレベルに合わせて簡単なRPGゲームの機能追加を行う複数のミッションに挑戦してもらいます。
コンソール画面を見ながら、実行テストを行っています。
みなさん初級は難なくクリアしていきますが、中級になると少し悩み始めました。
インストラクターもそれぞれ見回りながら、アドバイスをしていきます。
Ruby自体に慣れない人も多くなかなか苦戦されている方も多いです。
なお、侍エンジニアOnlineのイベントでは、毎回Skypeを使った参加も可能です。
今回はSkype参加者より出た「なぜオブジェクト指向言語が開発されたのか?」という疑問に対して、インストラクターがミッション中にレクチャーをしていました。
なお、ワークで製作したRubyのコードのサンプルコードを以下に示します。
全ての機能は実装していません。
残りの機能は、皆さんが実際に制作されてください。
class Battler def initialize(battlername) @name = battlername end def attack_point 10 end attr_accessor :name attr_accessor :attack_message attr_accessor :hp attr_accessor :ap end class Hero < Battler # 拡張とポリモルフィズム def get_downed_message "うわぁぁぁぁー!" end # 拡張とポリモルフィズム def attack_point if rand(5) >= 3 # 1~5のランダム値が3以上か? 30 # クリティカルヒット else 10 # 通常攻撃 end end # 拡張とポリモルフィズム def attack sleep 2 @attack_message = "<<<<いくぜ!!!>>>" [ @attack_message, attack_point ] end end class Monster < Battler # 拡張とポリモルフィズム def get_downed_message "ぐぉぉぉぉぉー!" end # 拡張とポリモルフィズム def attack_point if rand(5) >= 4 #1~5のランダム値が4以上か? 20 # クリティカルヒット else 15 # 通常攻撃 end end # 拡張とポリモルフィズム def attack sleep 2 @attack_message = "<<<<GURRRR>>>" [ @attack_message, attack_point ] end end hero = Hero.new("勇者") monster = Monster.new("ガーゴイル") attack = hero.attack puts hero.name + "の攻撃!" puts attack[0].to_s puts attack[1].to_s + "のポイントを与えた!" attack = monster.attack puts monster.name + "の攻撃!" puts attack[0].to_s puts attack[1].to_s + "のポイントのダメージを受けた!"
実行結果:
勇者の攻撃! <<<<いくぜ!!!>>> 30のポイントを与えた! ガーゴイルの攻撃! <<<<GURRRR>>> 15のダメージを受けた!
オブジェクト指向は理解できましたか?
1時間、ミッションとの戦いを終えたみなさんに、
・どこまで進むことができたのか?
・どんな気づきがあったのか?
・実際に作ったもの
以上の内容を発表していただきました!
Ruby自体の学びや、オブジェクト指向の概念に関する理解などそれぞれに得て頂けるものがあったようです。
やはり、なかなか難しい!と感じられる方もいらっしゃいましたが、自分なりに出来るところからとりあえず触ってみることの大切さもご理解いただけたようです。
中にはRPGの改造でセリフが順番に出るようにするなど凝った改造をされる方も出てきて盛り上がりました。
実際、あんなことやってみたい!こんなことできないかな?と、自分で考えて、自分で調べて、自分で実装してみるのが何よりも勉強になります。
それぞれの気づきをシェアしていただくことで、全体としての学びの相乗効果もありました。
本日もありがとうございました!
今回はワーク中心のセミナーだったこともあり、非常に充実度が高いイベントとなりました。
特に、ゲームの機能を実装する経験はプログラミング中級〜でも簡単には体験できないため、パラメータをいじったり、文言を出力させる小さな実装でもすごく楽しみながら進めていただけたようです。
今後はクラスやインスタンスを意識して、どのようにコードを書くことで「綺麗なコード」になるのか。その点を抑えて、できるエンジニアへと成長してもらえればと思います!
本日はご参加いただき、ありがとうございました!
参加者の声
参加者の方からイベントがどのように役立ったのか、お声をいただいていますのでご紹介いたします。
良かったこと:家にいても参加が可能だったこと。会場が自宅からは少々遠いので、助かりました(石川孟さん)
井上さんが定期的に巡回しているので、イベント外の質問も出来て良いと思います。(櫻井智行さん)
演習の題材が良く楽しみながら出来ました。メンバーが良い人達でした。(吉岡寿将さん)
ゲーム作りの中で、最も苦手なオプジェクト指向にスポットをあてて、詳しく解説して頂いたので凄く為になりました。(佐野さんの解説はとても丁寧で、わかり易かったです。)
これまで趣味でVC++でゲームを作ってましたが、関数は共通のところは使い回しをするものの、オプジェクト指向は全く使用して無かったです。
なので、規模が大きくなるに連れて、新しい処理の追加や、変更などするとグローバル変数などが勝手に変更されてしまい、バグ探しに苦労していました。
次回は、ぜひ画像キャラを読み込んだり、動かしたり、音を鳴らしたりできると楽しいと思います(*゚▽゚*)(Y.Yさん)
モチベーションを保てやすいところ。ひとりだとモチベーションを保てないところをこういう講義ベースだとやらなきゃって気持ちになる。(石川拓海さん)
他にも沢山のお声を頂きました!
みなさん、ありがとうございます!大変うれしく思っております!
最後に
今回はセミナー形式のイベントレポートでしたが、侍エンジニアOnlineのイベント風景が伝わりましたでしょうか?
侍エンジニアOnlineでは、学んだり体験ができるイベントをほぼ毎週行っています。
10月後半から11月にかけては
・侍卒業生に学ぶ!プログラミング学習のコツとリアルな体験
・ポケモンGOを作って学ぶ!簡単AR実装で最新技術を体験しよう
・実践プチ起業!ゼロから誰でも1ヶ月で出来るサービスの始め方
・これからの時代に置いていかれない為のAWS超入門
これらのイベントを開催予定です!
今回のイベントに参加できなかった方も、定期的に学べるイベントを用意していますので、ぜひご参加いただければと思います!
また、首都圏以外の方もSkypeでの参加が可能です!
同じ時間で一緒に学べる良い機会ですので、ぜひともご参加くださいませ!
それでは、また次回のイベントレポートをお楽しみに!