オブジェクト指向の考え方

オブジェクト指向の考え方
竹内 亮太
はじめに

この資料は、オブジェクト指向プログラミ
ングの理解を補助するためのものです
世の中には「オブジェクト指向とは何か」を
説いた書籍や資料は多いですが、オブジェクト
指向への移行の仕方や、その具体的な意味や
意義について述べたものは少ないのが実情です
 なので、作ってみました

前提知識

プログラミング演習の内容は大前提です
変数とは何か
 配列とは何か
 メソッドとは何か

 引数や返値までしっかり復習すること


その他、条件分岐や繰り返しの構文
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 クラス名();
として、クラス型変数を宣言して利用する
こうして作ったクラス型変数を
「インスタンス」と呼ぶ
 インスタンスを作ることを「実体化」とも言う

値をまとめたその次は

例:キャラクターのレベルアップ処理
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 パラディン();

子は親の一種として扱うことができる
続きは本編で

本編の深度に応じて続きも書きます