オブジェクト指向の考え方竹内 亮太
はじめに この資料は、オブジェクト指向プログラミ
ングの理解を補助するためのものです 世の中には「オブジェクト指向とは何か」を
説いた書籍や資料は多いですが、オブジェクト指向への移行の仕方や、その具体的な意味や意義について述べたものは少ないのが実情です
なので、作ってみました
前提知識 プログラミング演習の内容は大前提です
変数とは何か 配列とは何か メソッドとは何か
引数や返値までしっかり復習すること その他、条件分岐や繰り返しの構文
Java 言語に限定した内容ではありません C++ 言語などにも応用できます
理解のフェイズ オブジェクト指向は、フェイズ ( 段階 ) に
分けて理解していくことをおすすめします 自分に理解できて応用できそうな要素を、
段階を踏んで導入していけばよいでしょう
一度理解したフェイズも、時間をおいて復習してみましょう 理解の質が変わってくるはずです
変数のおさらい 値を覚えておくための箱のようなもの
1 つの箱に1つだけ、値を入れておける 入れる値の種類に応じて、違う型を使う
整数なら int 型、実数なら double 型など
iA
int iA = 12;
12 int 型
変数の限界 変数 1 つには、 1 つの値しか入れられない だが実際には、 1 つの数値だけでは表しき
れない物事が多数存在する
例 :RPG のキャラクターステータス 名前、職業、レベル、 HP 、 MP 、力、素早さ
、 etc… 例 : 三角形や長方形などの図形情報
幅と高さ、のように複数の値でないと表現不可
キャラクターを表現してみよう 右のようなステータス
画面のゲームの場合String sName = “ 剣士子” ;String sClass = “Swordman”;int iLevel = 4;int iExp = 0;int iNowHP = 150;int iMaxHP = 150;int iStr = 31, iVit = 28;int iDex = 14, iAgl = 11;int iInt = 7, iWis = 10;
だいたいこんな感じでできています
剣士子剣士子
SwordmanSwordman
キャラクターは 1 人じゃない さっきのゲームが 4 人
パーティでプレイするゲームだったとする
あと 3 人分も、あんな感じで変数を用意するのか?
… やってらんねぇ!
String charaB_sName = “ エルフっ娘” ;
String charaB_sClass = “Archer”;
int charaB_iLevel = 54;int charaB_iExp = 65535;int charaB_iNowHP = 420;int charaB_iMaxHP = 420;int charaB_iStr = 123,
charaB_iVit = 119;int charaB_iDex = 241,
charaB_iAgl = 180;int charaB_iInt = 176,
charaB_iWis = 155;
↑ これがあと 2 キャラ分必要になる
「クラス」の誕生 複数の値で何か 1 つの対象を表す時に、
その値をまとめておきたい まとめた上で、そのまとまったものを
「 1 つの変数として」扱えたら便利かも?
じゃあ必要な変数をまとめたものを「クラス」と呼ぶことにしよう 作ったクラスは int や double みたいに、
「変数の型の 1 種類」として扱えることにしよう
クラスの作り方 だいたい授業で説明したとおり
public class Character {public String sName;public String sClass;public int iLevel, iExp;public int iNowHP, iMaxHP;public int iStr, iVit, iDex, iAgl, iInt, iWis;
}
この段階では、まだ「新しい型の変数の箱」を設計した ( デザインした ) だけにすぎない
クラスの使い方 「クラス型変数」には、自分で決めた「メンバ変数
」の分だけ、別々に値をしまうことができる こうやって使う
Character charaA = new Character();charaA.sName = “ 剣士子” ;
以下のようなイメージ ( 一部メンバ変数だけ抜粋 )
String 型
Character 型
String 型 int 型 int 型 int 型sName sClass iLv iHP iStr
charaA
量産しよう、そうしよう 一回作ったクラスは、
いくらでも量産して使い回しが可能
Character charaB = new Character();
Character charaC = new Character();
Character charaD = new Character();
変数 1 つでキャラクター 1 人を表せている
String 型
Character 型
String 型 int 型 int 型 int 型
sName sClass iLv iHP iStr
charaB
String 型
Character 型
String 型 int 型 int 型 int 型
sName sClass iLv iHP iStr
charaC
String 型
Character 型
String 型 int 型 int 型 int 型
sName sClass iLv iHP iStr
charaD
フェイズ 1: クラスとは 何かを表すための値の集まり
値の集まりを、 1 つの変数のように扱える クラスを作るということは、オリジナルの変数
の箱をデザインするようなもの 作ったクラスを利用する時は、
クラス名 変数名 = new クラス名 ();
として、クラス型変数を宣言して利用する こうして作ったクラス型変数を
「インスタンス」と呼ぶ インスタンスを作ることを「実体化」とも言う
C++ の場合 変数の作り方が 2 種類ある
状況に応じて使い分けが必要
静的な作り方 ( らくちん ) Character chara1;
動的な作り方 (Java 的 ) Character *chara1 = new Character; C++ では new したら後片付け (delete) が必
要詳細は今後述べる
値をまとめたその次は 例 : キャラクターのレベルアップ処理
charaA.iLevel++;charaA.iMaxHP+=20;charaA.iStr+=5;…
いちいち「 charaA.~」とするのは面倒 charaB に対しても同じように書かなくては
ならないなら、値をまとめた意味が薄くなる 値をまとめたら、それに関する処理も
まとめたくなってくるむしろまとめるべき
メソッド化しよう クラスのメンバに関する処理は、
可能な限りメソッド化する レベルアップ処理のメソッド
public void incremntLevel() {iLevel++;iMaxHP+=20;iStr+=5;…
}
呼び出し側からの呼び出しcharaA.incremntLevel(); // charaA がレベルアップcharaB.incremntLevel(); // charaB がレベルアップ
メソッド化のメリット同じような処理を何度も書かずに済む
お決まりの手続きを 1 回の呼び出しで済ませる 複数のオブジェクトに対しても、呼び出し一発
メンバ変数を好き勝手にいじらせない メソッドの中で値をチェックすることで、
入れて欲しくない値を弾くことができる HP がマイナスになったらまずい、などなど
直接操作と間接操作 例 :ダメージを与える処理
HP が 20 の時に、 50 のダメージが当たった メソッドを使わない場合
charaA.iNowHP-=50; // 現在 HP が -30 になる
メソッド化した場合public void damage(int argDmg) {
iNowHP-=argDmg; if(iNowHP < 0) { iNowHP = 0; }}
~~(↑ はクラスの内部・↓は呼び出し側 )~~charaA.damage(50); // マイナスになる心配がなくなる!
アクセス制御 せっかくメソッドを用意しても、どこかで直接値をいじられたら意味がない
クラスの外からいじれるもの、いじって欲しくないものを分ける必要が出てくる
外からいじってもいいメンバには public を型の前につける
外からいじられたくないメンバには private を型の前につける
全部 public じゃダメなん?ダメです
小規模なプログラムでは問題ないですが、好き勝手にいじれる状態にしておくと、後々混乱の原因になります
基本的に変数は private にしておく各変数に対しては、値をセットしたり返してく
れたりするメソッドを public で用意しておく
メンバ変数を読み書きするためのメソッドを、 setter や getter と呼ぶ
いちいち set したり get したり 面倒ですよね
本来ならば、間に値をチェックしたりする必要がなければ、直接代入したり参照しても構いません ( クラスの利用目的にもよります )
ですが、今は練習がてらアクセッサ (setter & getter) を作る作業をして、慣れてください
特別なメソッド :コンストラクタ クラス型変数を new するのと同時に、
入れておきたい値、しておきたい処理を書いておくメソッド キャラクターなら、レベル 1 のステータスを
入れておきたい、など クラスと同じ名前のメソッドを作ると、
それがコンストラクタになる 返値の型は指定しない
コンストラクタは引数を取れる 引数を付けたら、 new する時に引数を渡す必
要がある
コンストラクタの例 キャラクターを作る時に、名前と職業だけ
決めて、後のメンバ変数にはレベル 1 のステータスをセットしたい// コンストラクタの作成例public Character(String argName, String argClass) {
sName = argName;sClass = argClass;iLevel = 1;…
}~~(↑ はクラスの内部・↓は呼び出し側 )~~// new する側はこんな感じCharacter charaA = new Character(“ エルフたん” , “Archer”);
フェイズ 2: メソッドとアクセス制御 クラスのメンバ変数に対する処理をまとめ
たものを、メソッドと呼ぶ メンバ変数とメソッドを持つことで、
クラスの本来の意義が完成する メンバ変数へのアクセスはメソッドを通じて行い、直接いじったりはしない それを徹底させるため、 public や private な
どのキーワードを指定し、アクセスを制御する new するついでに色々やらせることが可能
コンストラクタ
次のフェイズに向けて次のフェイズはちょっと次元跳躍します
これまでの内容でも、オブジェクト指向のメリットは大きいはずです十分プログラムの書き方は効率的になってます
ですが、これからが真骨頂です
しっかり復習してから進みましょう
色んなものをクラスで表そう 例 : 図形
三角形 長方形 円 直方体 球 円錐 …
例 :RPG のキャラクター 戦士 ナイト 僧侶 魔法使い ハンター 盗賊 アサシン 司祭 賢者 パラディン …
共通点があるもの多いよね? 図形は全て「図形」という括りにできる
更に言うなら平面図形と立体図形に分けられる
職業も、全て「キャラクター」で括れる更に言うなら、各職業には「上位」のものが
あったりするナイト→パラディン僧侶→司祭魔法使い→賢者
もっとクラス化を効率よく 考え方その 1
共通点だけを括りだしてクラスにする 例:「図形」という抽象的なクラス
それに個別のメンバを足して、「継承」したクラスを作る
例:「三角形・直方体」などの具体的なクラス
考え方その 2 基本的な機能 ( 能力 ) だけを持ったクラス
例:「ナイト・僧侶・魔法使い」など それを「継承」し、追加機能 ( 能力 ) を持たせたクラ
ス 例:「パラディン・司祭・賢者」など
表現する物事を体系化する 図形
平面図形 長方形 三角形 円
立体図形 直方体 円錐 球
キャラクター ナイト
パラディン 僧侶
司祭 魔法使い
賢者 ハンター
レンジャー シーフ
アサシン
「継承」のメリット共通部分をわざわざ書かなくてよい
付け加えたり、置き換えたい部分だけ作る 変数の追加 メソッドの追加 メソッドの置き換え
こういうことができる↓継承元クラス 変数名 = new 継承先クラス ();
図形 shape = new 長方形 ();ナイト chara0 = new パラディン ();
子は親の一種として扱うことができる