Object Oriented Programming
オブジェクト指向プログラミング

注意事項

毎回の授業の最初に出席メールを提出せよ.

前回までの出席記録を確認せよ.出席メールを授業時間内に 送信したのに記載されていない学生はメールの自分宛のコピーを印刷し,宛先アドレス, 提出時間が間違っていないことを確認してから TA に申し出よ.

課題と提出状況のページで課題の提出状況を確認せよ.

6 章 オブジェクト

オブジェクト指向プログラミングの特徴はデータとそれを扱う関数を まとめて管理できること.データとそれを扱う関数をオブジェクトと呼ぶ.

例えば掲示板プログラムで,個々のユーザをオブジェクトと対応付けると プログラムが分りやすくなる.ユーザ名やパスワードなどのユーザに関するデータと そのユーザの投稿記事を検索する,などの関数をまとめて雛形を作り,クラスとして記述する.

オブジェクト指向プログラミングの利点として,データとそれを扱う関数がセットになっているので 再利用しやすい点があげられる.

6.1 用語の定義

クラスとは格納するデータと関数の雛形であり, オブジェクトはその雛形に実際に データをもたせたもの.整数型のデータ型に対して具体的に値をもたせた整数型変数を 複数作ることができるのと同様に,ある 1 つのクラスを具体化したオブジェクトを複数 作ることができる.

オブジェクトに関連付けられた変数をプロパティ,関数をメソッド と言う.慣れるまではプロパティは変数,メソッドは関数と読み替えて理解する.

従来のプログラムではプロパティにあたる変数の内容を主体にメソッドにあたる関数を適用する という考え方でプログラムを作成するが,オブジェクト指向プログラミングではオブジェクトを 主体に,そのオブジェクトのプロパティ,そのオブジェクトのメソッド,という意識でプログラムを 作成する.

カプセル化とはオブジェクトに対して行う操作をあらかじめ 吟味し,必要になると考えられる操作をそのクラスのメソッドとして 用意しておくことをいう.外部からは統一された方法でアクセスされるので 問題が起こったときに問題点の分割ができ,デバッグがやりやすい. 再利用にも有利である.

クラスを作る際も,ゼロから作るだけではなく,既存のクラスを拡張する方法で クラスを作ることもできる.この概念を継承という.継承するのに 用いた既存のクラスをスーパークラス,新しく作成するクラスを サブクラス という.再利用に有利である.

以下は name, age というプロパティ,get_name(), set_name() というメソッドをもつクラス Person を定義し,使用する例である. 動作を確認せよ.

例 13

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="ja">
  <head>
    <title>Sample of OOP</title>
  </head>

  <body>
    <p>
<?php
class Person {
  private $name, $age;

  function get_name(){
    return $this->name;
  }

  function set_name($new_name){
    $this->name = $new_name;
  }

  function get_age(){
    return $this->age;
  }

  function set_age($new_age){
    $this->age = $new_age;
  }
}

  $p1 = new Person;
  $p1->set_name('Fred');
  $p1->set_age(35);
  $p2 = new Person;
  $p2->set_name('Barney');
  $p2->set_age(30);
  $name = $p1->get_name();
  $age = $p1->get_age();
  echo "$name $age <br>";
  $name = $p2->get_name();
  $age = $p2->get_age();
  echo "$name $age <br>";
?>
    </p>
  </body>
</html>

6.2 オブジェクトの作成

new でオブジェクトを作成する.new するときに引数を渡し, 初期データのセットなどが行える.やりかたは 6.4.7 節で説明する.

6.3 プロパティおよびメソッドへのアクセス

-> でオブジェクトのプロパティ,メソッドにアクセスできる.

public, protected, private 修飾子でアクセスコントロールができる. 違いは後で説明する.

static キーワードでスタティックなプロパティやメソッドを作ることができる. スタティックなプロパティ,メドッドはオブジェクトが無くても利用できる.

new で渡されるのはオブジェクトへのポインタである.この値をコピーしても 参照渡しとなる.コピーするためには clone 演算子を使う.

6.4 クラスの宣言

クラス,プロパティ,メソッドの宣言は例 13 参照.

クラスはデータ型に相当する雛形で,オブジェクトは具体的な値をもったデータである. あるクラスから複数のオブジェクトが作成されているとする.別々のオブジェクトから クラス内で宣言されているあるメソッドを呼び出したとき, クラス定義は 1 つであるから同じコードが実行される. $this を使うことでどのオブジェクトから呼び出されたのかを知ることができる.

例 13 では $this の働きで $p1 から呼ばれたときは $p1 のプロパティ(name, age)を操作し,$p2 から呼ばれたときは $p2 の プロパティを操作する.(スタティックでない)メソッドが呼び出されるときは必ず 何らかのオブジェクトに対して -> を使って呼び出される.この呼出しに使われた オブジェクトが $this に設定される. 例えば,$p1->get_name() として呼ばれてクラス定義内の get_name() プロパティのコードが実行されるとき,$this に $p1 がセットされていると理解すればよい.

スタティックメソッドでは対象のオブジェクトが存在しないため上記の理由から $this は使えない.

そのクラスではどのオブジェクトかによらず処理が 共通であるような関数はスタティックメソッドとして宣言できる. function の前に static と書く.呼出し方は教科書 p. 158 参照.

クラスを継承し,サブクラスを作った場合,もとあったクラスであるスーパークラスのメソッドと 同名のメソッドをサブクラスで宣言することでその名前のメソッドをオーバーライド(上書き) することができる.これを禁止するためには function の前に final を書く.

プロパティを宣言する場合 public, protected, private として宣言できる. public で宣言されたプロパティはクラス外部からアクセスできるが private で宣言されたプロパティはそのクラスからしかアクセスできなくなり, 安全性が高まる. 例 13 はクラスに用意されたメソッド set_name(), get_name(), set_age(), get_age(), を通してしかプロパティ $name, $age を変化 させない.外部から直接プロパティ $name, $age にアクセスしないで 済むように書かれているので $name, $age を private として宣言できる. public で宣言すると例えばオブジェクト $p1 については $p1->age = 0; のように直接アクセスされる可能性があるが private であればそれができないため安全で デバッグしやすい書き方である.protected は同じクラスかその派生クラスからしか アクセスできない宣言である.

スタティックメソッドと同様に,オブジェクトによらず共通の値 であるようなプロパティはスタティックプロパティとして宣言できる. 例えば Person クラスについて,現在登録されている人数,という データは意味的に個々のオブジェクトによらず Person クラスで一つ のデータであるからスタティックプロパティにあたる. スタティックプロパティはオブジェクトが複数あってもクラスに対して 1 つの変数である.呼出し方は p.159 参照.

PHP では存在しないプロパティにアクセスするとその時点でそのプロパティを 作成する.例 14 を実行し,プロパティ b が作成されることを確認せよ.

例 14

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="ja">
  <head>
    <title>Sample 14</title>
  </head>

  <body>
    <p>
<?php
class Sampleprp {
  public $a;
}

$obj = new Sampleprp;
$obj->a = 'valuea';
echo "{$obj->a}<br>";
$obj->b = 'valueb';
echo "{$obj->b}<br>";
?>
    </p>
  </body>
</html>

存在しないプロパティへのアクセスがあったとき, 自動でプロパティを生成してほしくない場合も多い. __get() メソッドや __set() メソッドを作っておくと存在しないプロパティへの アクセスがあった場合の処理を自分で決めることができる. エラー処理などに利用できる.存在しないプロパティ名 x を読み出そうとすると __get() メソッドが x を引数として呼ばれる.また存在しないプロパティ名 x にデータ v を書き込もうとすると __set() メソッドが,x, v を引数として呼ばれる. 例 15 で確認せよ.

例 15

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="ja">
  <head>
    <title>Sample 15</title>
  </head>

  <body>
    <p>
<?php
class Sampleprp {
  public $a;

  function __get($property){
      echo "Illegal read, property:$property<br>";
  }
  function __set($property, $value){
      echo "Illegal write, property:$property value:$value<br>";
  }
}

$obj = new Sampleprp;
$obj->a = 'valuea';
echo "{$obj->a}<br>";
$obj->b = 'valueb';
echo "{$obj->b}<br>";
?>
    </p>
  </body>
</html>

教科書の例は巨大なデータになるプロパティ $biography を,普段は 存在しないプロパティとしておき,アクセスがあった時だけ __get(), __set() を使って処理する例である.

変更されたくないデータは const で定数として宣言できる.呼出し方は スタティックプロパティと同様.

extends キーワードで既存のクラスを継承した新しいクラスを作ることができる. ゼロから設計せずに,似たクラスを探して足りない部分だけ追加することで クラスを作ることができる.教科書 p.160 の例では Person を継承した 新しいクラス Employee を定義している.Employee はプロパティとして スーパークラス(継承元)の Person がもつ $name, $addres, $age を自動的にもつ.その上, $position, $salary というプロパティを 追加している.同様にメソッドもスーパークラスのメソッドすべてを自動的にもち, 新たなメソッドを追加できる.

スーパークラスにあるプロパティ,メソッドと同じ名前のプロパティ,メソッドを 定義すると上書きされ,サブクラス(継承した新クラス)の定義が優先される. サブクラスからスーパークラスのほうの定義のメソッドを使用するときは parent を使う(p.160 最後参照)

self で現在のクラスのメソッドが呼び出される.(p.161 参照)

interface でこれから作るクラスの外部仕様だけ宣言することができる. 実際にクラスを作るときは implements を使う(教科書 p.161 最後)

抽象メソッドとは,メソッドの宣言だけ行って実装を行わないメソッドで, 実際には継承したサブクラスで実装を行う.一つでも抽象メソッドを含むクラスは 抽象クラスとなる.

抽象クラスとインターフェースは仕様だけ決めて実装しない,という 似た目的で使われるが,抽象クラスでは必ずクラスの親子関係を作る必要がある 点が異なる.入出力の仕様は同じだが直接関係ないので親子関係をもつクラスには したくない場合にインターフェースを使う.

例 13 のように,new でオブジェクトを作成するとすぐにプロパティ に初期値を設定するケースが多い.このため new したときにすぐにプロパティへの 代入が行われる仕組みがある.それをコンストラクタという.

例 13 の Person クラスのメソッド群の中に,以下のコンストラクタと 呼ばれる特別なメソッドを追加する.

例 13 の変更部分

  function __construct($name, $age) {
    $this->name = $name;
    $this->age = $age;
  }

例えば $p1 = new Person('Fred', 35); とすれば Person クラスのオブジェクト $p1 が作られるときに初期値として $name プロパティに 'Fred', $age プロパティに 35 が設定される.

例題 5

例 13 を上記コンストラクタを使用したものに書き換え,実行テストを行え.

コンストラクタとは逆にオブジェクトが破棄される時に実行される特別な関数 __destruct()がある.

6.5 内部検査

内部検査とは実行時にそのクラスの性質,もっているプロパティ,メソッドを 調べることである.この能力を使ってどんなプロパティ,メソッドが定義されているか わからないクラスに対して処理を行うことができる.

class_exists() は文字列を引数として,その名前のクラスが存在するかどうかを返す. get_declared_classes() は定義されているクラスすべてを配列で返す.その他の関数 は教科書 p.164 参照.

6.5.1 節の関数はクラスを対象とした調査で 6.5.2 節の関数はオブジェクトを 対象とした操作.

6.5.3 節に,デバッグに利用できるサンプルが集められている.

6.6 シリアライズ

HTTP は基本的には状態を記憶しないプロトコルである. 処理内容を記憶させるには接続の最後にデータをファイルに保存し, 次の接続時にそれを読み出す仕組みが必要である. PHP ではこのファイルへの保存,ファイルからの読み出し処理を行う便利な セッション機能が用意されている. セッションによるファイルへの保存の前段階として複雑なデータである オブジェクトをファイルに保存できるデータに変換する必要がある.これが シリアライズである.シリアライズ処理のために serialize() 関数が用意 されている.serialize() で処理されたデータを復元する関数が unserialize() である.PHP のセッション機能を使えばこれらを自動的に行ってくれる.

p.172-174 の front.php と next.php は register_grobals = Off の環境では動かない. セキュリティの問題(p.316, 12.5.3 節)からこのサンプルのような 使用法は現在推奨されていない. front.php と next.php を以下のように変更すると動作する.

修正版 front.php

<?php
 include_once('Log.inc');
 session_start();
?>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="ja">
<head>
<title>フロントページ</title>
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
</head>
<body>
<?php
 $now = strftime("%c");

 if (!isset($_SESSION['l'])) {
   $_SESSION['l'] = new Log("/tmp/persistent_log");

   $_SESSION['l']->write("作成時刻 $now");
   echo("<p> セッションおよびログオブジェクトを作成しました.</p>");
 }

 $_SESSION['l']->write("最初のページの読み込み時刻 $now");
 echo "<p>ログの内容</p>";
 echo nl2br($_SESSION['l']->read());
?>

<a href="next.php">次のページへ</a>

</body></html>

修正版 next.php

<?php
 include_once('Log.inc');
 session_start();
?>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="ja">
<head>
<title>次のページ</title>
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
</head>
<body>
<?php
 $now = strftime("%c");
 $_SESSION['l']->write("2 ページ目の表示時刻 $now");

 echo "<p>ログの内容</p>";

 echo nl2br($_SESSION['l']->read());
?>

第 6 回課題「メソッドの作成」 を提出せよ.


Updated in June 1, 2010, index.html, Yamamoto Hiroshi