オブジェクト指向

出典: へっぽこ実験ウィキ『八百科事典(アンサイクロペディア)』
移動: 案内検索
Wikipedia
ユーモア欠落症患者のために、ウィキペディア専門家気取りたちが「オブジェクト指向」の項目を執筆しています。
Bouncypotato.gif
この記事「オブジェクト指向」は何故か「オブジェクト指向 (情報工学)」とネタや題材がダブっています。どちらが真実なのかは神のみぞ知ります。
Bouncypotato.gif
この記事「オブジェクト指向」は何故か「Объект指向」とネタや題材がダブっています。どちらが真実なのかは神のみぞ知ります。

オブジェクト指向(オブジェクトしこう)とは、アニメギャルゲーエロゲーなどについて、仮想的なキャラクターと中の人を別々に取り扱うことで抽象度を上げ、保守性を向上させる手法である。また、オブジェクト指向を扱うように設計されたプログラミング言語を、オブジェクト指向言語と呼ぶ。

基本概念[編集]

概要[編集]

多くのオブジェクト指向言語は、中の人をクラスとして定義する。まず「男キャラ」「女キャラ」のようにして中の人を宣言し、「女キャラ」を実体化して「香織」「素子」などを作る(実体化したものをインスタンスと呼ぶ)。実体には、パラメータなどを格納することができる(これをメンバ変数と呼ぶ)。後述するコード例で、「香織」「素子」は、パラメータは異なっているが、中の人は同じである。

実体へのアクセスは、かならず中の人を通しておこなう。これには「会う」「話す」などのメソッド(メンバ関数とも呼ぶ)と呼ばれる関数を定義しておこなう。

コード例[編集]

以下は、PHP5によるコード例である。

class	キャラ {
	var	$今日は話した = 0;
	var	$好感度 = 0;
	var	$難易度 = 10;
	function	キャラ($難易度 = 10) {
		$this->難易度 = $難易度;
	}
	function	会う() {
		$this->今日は話した = 0;
		return "やあ";
	}
	function	話す() {
		if (($this->今日は話した)) {
			$this->好感度--;
			return "しつこい";
		}
		$this->今日は話した = 1;
		$this->好感度++;
		return "別に";
	}
	function	誘う() {
		if ($this->好感度 >= $this->難易度) {
			$this->好感度++;
			return "いいよ";
		}
		$this->好感度--;
		return "やだ";
	}
	function	バイバイ() {
		return "またね";
	}
}

$香織 =& new キャラ(3);
$素子 =& new キャラ(6);

print "\n---- 1日目\n\n";
print $香織->会う()."\n";
print $香織->話す()."\n";
print $香織->バイバイ()."\n";

これだけではオブジェクト指向の利点が見えないが、関数を使うと、異なる実体(インスタンス)が渡されても同じ処理ができるという利点が実感できる。

function 爆弾処理(&$相手) {
	print $相手->会う()."\n";
	print $相手->話す()."\n";
	print $相手->誘う()."\n";
	print $相手->バイバイ()."\n";
}

爆弾処理($香織);
爆弾処理($素子);

多態性[編集]

概要[編集]

オブジェクト指向言語では「継承」を利用することで、「中の人」に違う演技をさせることができる。このとき、演技全体を記述するのではなく、元との差分だけを記述すればよいので、保守性の向上が期待できる。また、継承元のクラスを呼び出し、引数や返り値を書き換えることも可能である。

コード例[編集]

「誘う」のみ異なる動作をさせるには、以下のように記述する。

class	サブキャラ extends キャラ {
	function	誘う() {
		return "ごめんなさい";
	}
}

$秋子 =& new サブキャラ(3);

print "\n---- 1日目\n\n";
print $秋子->会う()."\n";
print $秋子->話す()."\n";
print $秋子->バイバイ()."\n";

また、「キャラ」からの返り値を書き換えて返す「っすキャラ」は、以下のように定義する。

class	っすキャラ extends キャラ {
	function	会う() {
		return parent::会う()."っす";
	}
	function	話す() {
		return parent::話す()."っす";
	}
	function	誘う() {
		return parent::誘う()."っす";
	}
	function	バイバイ() {
		return parent::バイバイ()."っす";
	}
}

$美紗 =& new っすキャラ(3);

print "\n---- 1日目\n\n";
print $美紗->会う()."\n";
print $美紗->話す()."\n";
print $美紗->バイバイ()."\n";

継承後の中の人の実体は、継承前の中の人を扱う関数に渡すことができる。これはJavaなど静的な型宣言がある言語では厳密にチェックされる。下記の例で、爆弾処理の引数は「キャラ」の実体しか想定されていなかったが、「キャラ」を継承した「サブキャラ」や「っすキャラ」であれば渡すことができる。

爆弾処理($美紗);

このように、同じ「会う」であっても対象によって異なる動作をする、言いかえると異なる対象に同じ「会う」が適用できる性質を、多態性と呼ぶ。新キャラが追加され、「会う」がさらに変更された場合でも、それを呼び出す側の爆弾処理は変更する必要がないため、保守性が向上する。

動的多態性[編集]

概要[編集]

上記の例では、状態に応じて中の人が変わるようなことはできない。しかし、中の人を別のインスタンスにすることで、動的に中の人を変えることができる。

コード例[編集]

下記の例では、「中の人」から返る実体が、状態によって変化する。「デレモード」は「ツンモード」を継承しているため、どちらも共通に呼び出すことができる。パラメータは「ツンデレキャラ」に持っているため、中の人が変わっても状態は保持される。同じ考え方で「ネコミミモード」「月詠モード」も実装できる。

class	ツンモード {
	var	$parent;
	function	ツンモード(&$parent) {
		$this->parent =& $parent;
	}
	function	会う() {
		$this->parent->今日は話した = 0;
		return "な、なによ";
	}
	function	話す() {
		if (($this->parent->今日は話した)) {
			$this->parent->好感度--;
			return "バカ、しつこい";
		}
		$this->parent->今日は話した = 1;
		$this->parent->好感度++;
		return "べ、別に";
	}
	function	誘う() {
		$this->parent->好感度--;
		return "バカ、なに考えてるのよ";
	}
	function	バイバイ() {
		return "....行っちゃうの?";
	}
}

class	デレモード extends ツンモード {
	function	会う() {
		$this->parent->今日は話した = 0;
		return "....おはよう";
	}
	function	話す() {
		if (($this->parent->今日は話した)) {
			$this->parent->好感度--;
			return "しつこいわよ";
		}
		$this->parent->今日は話した = 1;
		$this->parent->好感度++;
		return "そうね";
	}
	function	誘う() {
		$this->parent->好感度++;
		return "ありがとうっ、楽しみね";
	}
	function	バイバイ() {
		return "また、会えるよね";
	}
}

class	ツンデレキャラ {
	var	$今日は話した = 0;
	var	$好感度 = 0;
	var	$難易度 = 10;
	var	$ツン;
	var	$デレ;
	function	ツンデレキャラ($難易度 = 10) {
		$this->難易度 = $難易度;
		$this->ツン =& new ツンモード($this);
		$this->デレ =& new デレモード($this);
	}
	function	&中の人() {
		if ($this->好感度 >= $this->難易度)
			return $this->デレ;
		return $this->ツン;
	}
}

$舞 =& new ツンデレキャラ(3);

print "\n---- 1日目\n\n";
print $舞->中の人()->会う()."\n";
print $舞->中の人()->話す()."\n";
print $舞->中の人()->バイバイ()."\n";

応用事例[編集]

途中で中の人が変わるケースとしては、以下のものが確認されている。

デザインパターン[編集]

概要[編集]

ソフトウェアは幅広い応用が可能である反面、ある人が最善だと考えた設計手法が、他の人に理解されやすいとは限らないという問題がある。オブジェクト指向では早期にデザインパターンと呼ばれる考え方が提唱され、対応が図られた。デザインパターンは、典型的な問題に対する一般的な解法を集めたものである。

コード例[編集]

上記の例で、「ツンデレキャラ」は「キャラ」を継承したクラスではないため、このままでは$舞を前述の爆弾処理に渡すことはできない。そこで、「キャラ」を継承して「中の人」を追加するように変更する。このように、異なる呼び出し方をする中の人に対応するために、橋渡しをするための中の人を用意する手法を「Adapterパターン」と呼ぶ。

# 差し替え
class	ツンデレキャラ extends キャラ {
	var	$ツン;
	var	$デレ;
	function	ツンデレキャラ($難易度 = 10) {
		parent::キャラ($難易度);
		$this->ツン =& new ツンモード($this);
		$this->デレ =& new デレモード($this);
	}
	function	&中の人() {
		if ($this->好感度 >= $this->難易度)
			return $this->デレ;
		return $this->ツン;
	}
	function	会う() {
		return $this->中の人()->会う();
	}
	function	話す() {
		return $this->中の人()->話す();
	}
	function	誘う() {
		return $this->中の人()->誘う();
	}
	function	バイバイ() {
		return $this->中の人()->バイバイ();
	}
}

$マヤ =& new ツンデレキャラ(3);
爆弾処理($マヤ);

さらに、「キャラ」に「中の人」を追加して「return $this;」としておけば、「キャラ」も「ツンデレキャラ」も同じように呼び出すことができる(「キャラ」を継承した「サブキャラ」や「っすキャラ」にも自動的に反映される)。「中の人」を使って機能を増やしていくことにしたが、「キャラ->会う()」のような使い方も残しておきたい場合などに利用できる。

ソフトウェア以外のデザインパターン[編集]

デザインパターンの考え方は、ソフトウェア以外にも幅広く応用されている。ここでは一例を取り上げる。

また、システム全体の汎用的な構造を標準化し、中のモジュールをカスタマイズすることで開発をおこなう、フレームワークと呼ばれる手法もある。

注意事項[編集]

ここでは最低限の語彙で説明するため、すべてクラスと継承で記述したが、実際にはインターフェースといった仕組みを使うのがよい場面もある。

PHPで変数名などに日本語を使用するには、文字コードをEUCかUTF-8にする必要がある。

PHP4では「$舞->中の人()->会う();」は利用できず、「$中の人 =& $舞->中の人();」「$中の人->会う();」の2行にわける必要がある。あるいは「中の人」というメンバ変数を作り、状態の変化に応じて適切なオブジェクトを代入していく方法がある。

PHP以外の言語でサンプルプログラムを動かすには、以下のような点に注意して移植する必要がある。

  • 変数名に$をつけるかどうか
  • オブジェクトの参照渡しに&を使うかどうか
  • メソッドの呼び出し方(PHPでは->)
  • 文字列連結演算子(PHPでは.)
  • 継承元メソッドの呼び出し方(PHPではparent::メソッド名)
  • メンバ変数を継承先でオーバーライドできるかどうか
  • オーバーライドされるメソッドに指定が必要か
  • 各種変数に型宣言が必要か
  • 変数名に日本語文字列が使用できるか
  • main関数をどこに書くか(PHPでは最外周スコープ)
  • ツンデレに対応しているか
  • 今日はネコミミモードか
  • 実の妹と関係を持つことができるか
  • 発売延期の可能性
  • 登場人物は18歳以上

関連項目[編集]


「オブジェクト指向」は抽象クラス(abstract class)で、クラスとしては不完全なためインスタンス化できません。使用するにはコードを追加してインスタンス化できるようにしてください。 (Portal:スタブ)