オブジェクト指向の考え方
Download
Report
Transcript オブジェクト指向の考え方
オブジェクト指向の考え方
竹内 亮太
はじめに
この資料は、オブジェクト指向プログラミ
ングの理解を補助するためのものです
世の中には「オブジェクト指向とは何か」を
説いた書籍や資料は多いですが、オブジェクト
指向への移行の仕方や、その具体的な意味や
意義について述べたものは少ないのが実情です
なので、作ってみました
前提知識
プログラミング演習の内容は大前提です
変数とは何か
配列とは何か
メソッドとは何か
引数や返値までしっかり復習すること
その他、条件分岐や繰り返しの構文
Java言語に限定した内容ではありません
C++言語などにも応用できます
理解のフェイズ
オブジェクト指向は、フェイズ(段階)に
分けて理解していくことをおすすめします
自分に理解できて応用できそうな要素を、
段階を踏んで導入していけばよいでしょう
一度理解したフェイズも、時間をおいて
復習してみましょう
理解の質が変わってくるはずです
変数のおさらい
値を覚えておくための箱のようなもの
1つの箱に1つだけ、値を入れておける
入れる値の種類に応じて、違う型を使う
整数なら
int 型、実数なら double 型など
int iA = 12;
12
int型
iA
変数の限界
変数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;
だいたいこんな感じで
できています
剣士子
Swordman
キャラクターは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型 String型 int型
sName sClass
iLv
Character型
charaA
int型
iHP
int型
iStr
量産しよう、そうしよう
一回作ったクラスは、
いくらでも量産して
使い回しが可能
String型
sName
String型
int型
int型
int型
sClass
iLv
iHP
iStr
Character型
Character charaB = new
Character();
Character charaC = new
Character();
Character charaD = new
Character();
変数1つでキャラクタ
ー1人を表せている
charaB
String型
sName
String型
int型
int型
int型
sClass
iLv
iHP
iStr
Character型
charaC
String型
sName
String型
int型
int型
int型
sClass
iLv
iHP
iStr
Character型
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();
charaB.incremntLevel();
// charaA がレベルアップ
// 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 パラディン();
子は親の一種として扱うことができる