極度の面倒くさがりにより、漠然とオブジェクト指向から距離を置いてきたが、最近初めて触れ、やっと理解できた気がするので書く。
世の人間はオブジェクト指向を説明するとき、たい焼きだの設計図だの何だのと言い始める。
そうじゃないんだ。読めるようになりたいんだ。
ということで、自分なりにまとめてみる。PHPを例にする。誰かの役に立てたら嬉しい。
まず、「オブジェクト指向」とはそもそも何かと言うと、設計思想の1つのことだ。
そしてこのオブジェクト指向を実現するため、「クラス構文」という書式がある。要はこれが使えればいいのだ。
「オブジェクト」とは関数と変数のカタマリの事である。関数と変数が梱包された状態だと思ってほしい。
このオブジェクトのことを「クラス」とも呼ぶ。「クラス構文」とは、つまりオブジェクトを定義する書式だと思えばいい。
※わかりやすくするため、ここでは「オブジェクト」=「クラス」とする。後述する「インスタンス」はオブジェクトと呼ばれやすいが、クラスとは別物。「オブジェクト」=関数と変数のカタマリの総称、「クラス」=オブジェクトを定義したもの、「インスタンス」=クラスをインスタンス化したもの、なんだなーと思っておけば大丈夫。
オブジェクトは定義して呼び出すのが基本で、こういう流れになる。
【定義する】
空っぽのオブジェクトを用意する。
⇓
オブジェクトの中に変数や関数を入れていく。
⇓
オブジェクトが定義できた!(オブジェクト完成!)
【呼び出す】
定義されたオブジェクトを読み込む。
⇓
オブジェクトから変数や関数を引っ張りだして使う。
オブジェクトは、中身の呼び出し方を考えてから定義することが多いので、先に呼び出し方を確認しておこう。
例として、引数に与えた文字をそのまま表示するユーザー定義関数foo()
を用意した。
function foo($str)
{
echo $str;
}
この関数を、どんな名前でもいいが、ここではMyProject
という名前のオブジェクトに入れることにする。(「オブジェクトを定義するには」で手順は後述)
早速オブジェクトMyProject
からfoo()
を呼び出してみる。
// 事前にオブジェクトが定義されているとする
$obj = new MyProject;
$obj->foo('おはよう!'); // おはよう!が表示される
最初の行を見てほしい。
$obj
という変数の中に、オブジェクトが代入されている。
そう、オブジェクトはnew 〇〇
という書式で、変数に一度代入することで使えるようになる。これを「インスタンス化」と呼び、このとき、変数は「オブジェクト型」という状態になっている。
つまり、上記の例ではオブジェクト型の変数$obj
に、事前に定義したオブジェクトMyProject
の中身がそのまま入っているのだ。
この代入された変数のことを「インスタンス」という。インスタンス=オブジェクト型の変数のことである。
続きを見てほしい。
$obj->foo('おはよう!')
とある。これは、オブジェクトのインスタンスからfoo()
というユーザー定義関数を呼び出している。
このとき、普通の関数とは呼び出し方が異なるので、この関数のことを特別に「メソッド」と呼ぶ。
やたら用語ばかり出てきて申し訳ない。先ほどのコードはこういうことである。
さて、ここで先ほどの例で使ったオブジェクトMyProject
を定義する手順だ。
最初に空っぽのオブジェクトを用意する。こう書く。
class MyProject
{
}
この中にさっき作ったユーザー定義関数foo()
を入れてみると…
class MyProject
{
function foo($str)
{
echo $str;
}
}
オブジェクトが完成!
これだけ?これだけ!
これで、MyProject
というオブジェクトの中にfoo()
というメソッドが定義されたことになる。
さっき呼び出しの部分で書いたものと組み合わせてみよう。
class MyProject
{
function foo($str)
{
echo $str;
}
}
$obj = new MyProject;
$obj->foo('おはよう!'); // おはよう!が表示される
これで一段落。これが理解できたあなたは、とても基本的なクラス構文をマスターしたことになる。
ここまで来た人は疑問に思うかもしれない。
「関数をメソッドにする意味って何なの?」「それ、ユーザー定義関数で良くね?」と。
ここからは、オブジェクト指向だからできること、プロパティの仕組みを説明する。
「オブジェクトって何?」で説明した図を思い出してほしい。
そう、梱包されてるのは関数(メソッド)だけじゃなかった。変数もいた。
このオブジェクトの中で定義された変数のことを、「プロパティ」と呼ぶ。
プロパティの基本的な定義の仕方と呼び出し方は次の通り。
class MyProject
{
public $text = 'おはよう!'; // これがプロパティ
}
$obj = new MyProject;
echo $obj->text; // プロパティの呼び出し
メソッドと似ていることがわかるだろうか?
そう、プロパティとメソッドは扱い方がとてもよく似ている。
今回は、最初に少し見慣れないキーワード「public」について説明しよう。
これは、メソッド(関数)やプロパティ(変数)のアクセス権を示している。
PHPではpublic, protected, privateの3つがある。
アクセス権 | 範囲 |
---|---|
public | オブジェクトの外部から呼び出せる。継承されたオブジェクトから呼び出せる。オブジェクト内のメソッドから呼び出せる。 |
protected | オブジェクトの外部から呼び出せない。継承されたオブジェクトから呼び出せる。オブジェクト内のメソッドから呼び出せる。 |
private | オブジェクトの外部から呼び出せない。継承されたオブジェクトから呼び出せない。オブジェクト内のメソッドから呼び出せる。 |
プロパティではこれらのアクセス権を必ず明示する必要がある。メソッドでは、省略した場合publicとしてみなされる。
つまり、前項の例で作ったメソッドは、publicを省略していたということだ。省略できるのだが、アクセス権はちゃんと書いておいたほうが喜ばれるだろう。
さて、プロパティで注意してほしいのは、メソッドの外でないと宣言できないということだ。
例えば、以下のようなことはできない。
class MyProject
{
// publicなメソッドを宣言
public function foo()
{
public $text = 'おはよう!'; // メソッド内なのでエラー
echo $text;
}
}
$obj = new MyProject;
echo $obj->foo();
これは、ユーザー定義関数の特性上、その中で使われる変数はローカルスコープでないといけないからだ。
もしメソッドの中でプロパティを使いたいのなら、$obj->text
みたいな感じで変数を呼び出さなければならない。だが、自分のオブジェクトの中で自分のオブジェクトを指定するのもおかしいよな…ということで、擬似変数$this
が登場する。
class MyProject
{
public $text = 'おはよう!';
public function foo()
{
echo $this->text; // $thisは自分自身を表す
}
}
$obj = new MyProject;
echo $obj->foo(); // おはよう!
$this
を使うと、プロパティを活かして、例えばこんなことができる。
class Cart
{
protected $quantities = []; // 商品ごとの量が入る
public function add($product, $quantity)
{
$this->quantities[$product] = $quantity;
}
public function get_quantity($product)
{
return $product . 'は' . $this->quantities[$product] . '個';
}
}
$my_cart = new Cart;
$my_cart->add('バター', 1);
$my_cart->add('牛乳', 3);
$my_cart->add('卵', 6);
echo $my_cart->get_quantity('牛乳'); // 牛乳は3個
合計金額を表示するメソッドも作れる。
class Cart
{
protected $prices = [
'バター' => 600,
'牛乳' => 180,
'卵' => 200,
'アイス' => 100
];
protected $quantities = [];
public function add($product, $quantity)
{
if(isset($this->prices[$product]))
{
$this->quantities[$product] = $quantity;
}
}
public function get_quantity($product)
{
return $product . 'は' . $this->quantities[$product] . '個';
}
public function get_total_price()
{
$total_price = 0;
foreach($this->quantities as $key => $quantity)
{
$total_price += $this->prices[$key] * $quantity;
}
return $total_price;
}
}
$my_cart = new Cart;
$my_cart->add('バター', 1);
$my_cart->add('牛乳', 3);
$my_cart->add('卵', 6);
echo $my_cart->get_total_price() . '円'; // 2340円
オブジェクト指向の便利さはここからだ。上の例の後半だけ次のように書き換える。
$cart1 = new Cart;
$cart1->add('バター', 1);
$cart1->add('バター', 3);
$cart1->add('卵', 6);
$cart1->add('卵', -3);
$cart2 = new Cart;
$cart2->add('アイス', 2);
$cart2->add('牛乳', 1);
echo $cart1->get_total_price() . '円'; // 1200円
echo $cart2->get_total_price() . '円'; // 380円
インスタンス$cart1
, $cart2
が別々のものとして計算されている。つまり、2つのオブジェクトは独立して動作しているのだ。
これがインスタンス化の最大のメリットである。もしこの処理をユーザー定義関数で無理やり実現しようとすると、それぞれの処理が複雑に干渉してしまい、たちまち地獄が訪れるだろう。
処理をいい感じに独立させることは、プログラミングをする上でとても大切だ。また、プログラムに将来への拡張性があることも、どれほど大切かわかる人は多いだろう。
オブジェクト指向はこれを実現するためにある。
インスタンス化で複数の機能をいい感じに独立させられる。またクラスの中にメソッドを追加しても他の処理に影響を与えないため、将来への拡張性も確保できる。
オブジェクト指向はプログラマの「あったらいいな」が詰め込まれているため、機能が多すぎて面食らってしまいやすいが、本質的な部分はとても簡単なものである。
継承や無名関数、コンストラクタなど、面白い機能が他にもたくさんあるので、興味があるものから一つずつ調べてみるのがいいだろう。
Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。
また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!
こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?
コメント