2019-09-16に更新

オブジェクト指向とは何だったのか(初心者として)

極度の面倒くさがりにより、漠然とオブジェクト指向から距離を置いてきたが、最近初めて触れ、やっと理解できた気がするので書く。

世の人間はオブジェクト指向を説明するとき、たい焼きだの設計図だの何だのと言い始める。
そうじゃないんだ。読めるようになりたいんだ。

ということで、自分なりにまとめてみる。PHPを例にする。誰かの役に立てたら嬉しい。

オブジェクトって何

まず、「オブジェクト指向」とはそもそも何かと言うと、設計思想の1つのことだ。
そしてこのオブジェクト指向を実現するため、「クラス構文」という書式がある。要はこれが使えればいいのだ。

image.png

「オブジェクト」とは関数と変数のカタマリの事である。関数と変数が梱包された状態だと思ってほしい。

image.png

このオブジェクトのことを「クラス」とも呼ぶ。「クラス構文」とは、つまりオブジェクトを定義する書式だと思えばいい。

※わかりやすくするため、ここでは「オブジェクト」=「クラス」とする。後述する「インスタンス」はオブジェクトと呼ばれやすいが、クラスとは別物。「オブジェクト」=関数と変数のカタマリの総称、「クラス」=オブジェクトを定義したもの、「インスタンス」=クラスをインスタンス化したもの、なんだなーと思っておけば大丈夫。

オブジェクトを使っていく流れ

オブジェクトは定義して呼び出すのが基本で、こういう流れになる。

【定義する】

空っぽのオブジェクトを用意する。

オブジェクトの中に変数や関数を入れていく。

オブジェクトが定義できた!(オブジェクト完成!)

【呼び出す】

定義されたオブジェクトを読み込む。

オブジェクトから変数や関数を引っ張りだして使う。

オブジェクトの中身を呼び出すには

オブジェクトは、中身の呼び出し方を考えてから定義することが多いので、先に呼び出し方を確認しておこう。

例として、引数に与えた文字をそのまま表示するユーザー定義関数foo()を用意した。

function foo($str)
{
    echo $str;
}

この関数を、どんな名前でもいいが、ここではMyProjectという名前のオブジェクトに入れることにする。(「オブジェクトを定義するには」で手順は後述)
早速オブジェクトMyProjectからfoo()を呼び出してみる。

// 事前にオブジェクトが定義されているとする
$obj = new MyProject;

$obj->foo('おはよう!');  // おはよう!が表示される

最初の行を見てほしい。
$objという変数の中に、オブジェクトが代入されている。

そう、オブジェクトはnew 〇〇という書式で、変数に一度代入することで使えるようになる。これを「インスタンス化」と呼び、このとき、変数は「オブジェクト型」という状態になっている。

つまり、上記の例ではオブジェクト型の変数$objに、事前に定義したオブジェクトMyProjectの中身がそのまま入っているのだ。

image.png

この代入された変数のことを「インスタンス」という。インスタンス=オブジェクト型の変数のことである。

続きを見てほしい。
$obj->foo('おはよう!')とある。これは、オブジェクトのインスタンスからfoo()というユーザー定義関数を呼び出している。

image.png

このとき、普通の関数とは呼び出し方が異なるので、この関数のことを特別に「メソッド」と呼ぶ。

やたら用語ばかり出てきて申し訳ない。先ほどのコードはこういうことである。
image.png

オブジェクトを定義するには

さて、ここで先ほどの例で使ったオブジェクト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('おはよう!');  // おはよう!が表示される

これで一段落。これが理解できたあなたは、とても基本的なクラス構文をマスターしたことになる。

オブジェクトにしかできないこと…プロパティ

ここまで来た人は疑問に思うかもしれない。
「関数をメソッドにする意味って何なの?」「それ、ユーザー定義関数で良くね?」と。

ここからは、オブジェクト指向だからできること、プロパティの仕組みを説明する。

「オブジェクトって何?」で説明した図を思い出してほしい。

image.png

そう、梱包されてるのは関数(メソッド)だけじゃなかった。変数もいた。
このオブジェクトの中で定義された変数のことを、「プロパティ」と呼ぶ。

プロパティの基本的な定義の仕方と呼び出し方は次の通り。

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つのオブジェクトは独立して動作しているのだ。

これがインスタンス化の最大のメリットである。もしこの処理をユーザー定義関数で無理やり実現しようとすると、それぞれの処理が複雑に干渉してしまい、たちまち地獄が訪れるだろう。

処理をいい感じに独立させることは、プログラミングをする上でとても大切だ。また、プログラムに将来への拡張性があることも、どれほど大切かわかる人は多いだろう。

オブジェクト指向はこれを実現するためにある。
インスタンス化で複数の機能をいい感じに独立させられる。またクラスの中にメソッドを追加しても他の処理に影響を与えないため、将来への拡張性も確保できる。

おわりに

オブジェクト指向はプログラマの「あったらいいな」が詰め込まれているため、機能が多すぎて面食らってしまいやすいが、本質的な部分はとても簡単なものである。
継承や無名関数、コンストラクタなど、面白い機能が他にもたくさんあるので、興味があるものから一つずつ調べてみるのがいいだろう。

PHPマニュアルおすすめ部分

PHP: クラスの基礎

PHP: プロパティ

PHP: アクセス権

PHP: 無名関数

ツイッターでシェア
みんなに共有、忘れないようにメモ

ウラル

Splatoonの二次創作サイト「スプランプ」の管理人です。サーモンラン研究所やオクトチャット、フェス速報などを作りました。

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント