tag:crieit.net,2005:https://crieit.net/tags/%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91/feed 「オブジェクト指向」の記事 - Crieit Crieitでタグ「オブジェクト指向」に投稿された最近の記事 2019-09-16T00:30:47+09:00 https://crieit.net/tags/%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91/feed tag:crieit.net,2005:PublicArticle/15398 2019-09-16T00:18:49+09:00 2019-09-16T00:30:47+09:00 https://crieit.net/posts/0ad3fb07d90d16ac9612cde5d92f6bed オブジェクト指向とは何だったのか(初心者として) <p>極度の面倒くさがりにより、漠然とオブジェクト指向から距離を置いてきたが、最近初めて触れ、やっと理解できた気がするので書く。</p> <p>世の人間はオブジェクト指向を説明するとき、たい焼きだの設計図だの何だのと言い始める。<br /> そうじゃないんだ。読めるようになりたいんだ。</p> <p>ということで、自分なりにまとめてみる。PHPを例にする。誰かの役に立てたら嬉しい。</p> <h1 id="オブジェクトって何"><a href="#%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%A3%E3%81%A6%E4%BD%95">オブジェクトって何</a></h1> <p>まず、「オブジェクト指向」とはそもそも何かと言うと、設計思想の1つのことだ。<br /> そしてこのオブジェクト指向を実現するため、「クラス構文」という書式がある。要はこれが使えればいいのだ。</p> <p><a href="https://crieit.now.sh/upload_images/f37dd84b36bcbac2749ed7d9d5d7127f5d7cecde17e98.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/f37dd84b36bcbac2749ed7d9d5d7127f5d7cecde17e98.png?mw=700" alt="image.png" /></a></p> <p>「オブジェクト」とは関数と変数のカタマリの事である。関数と変数が梱包された状態だと思ってほしい。</p> <p><a href="https://crieit.now.sh/upload_images/f37dd84b36bcbac2749ed7d9d5d7127f5d7ceecf2278b.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/f37dd84b36bcbac2749ed7d9d5d7127f5d7ceecf2278b.png?mw=700" alt="image.png" /></a></p> <p>このオブジェクトのことを「クラス」とも呼ぶ。「クラス構文」とは、つまりオブジェクトを定義する書式だと思えばいい。</p> <p style="font-size:smaller;margin:0;padding:0;">※わかりやすくするため、ここでは「オブジェクト」=「クラス」とする。後述する「インスタンス」はオブジェクトと呼ばれやすいが、クラスとは別物。「オブジェクト」=関数と変数のカタマリの総称、「クラス」=オブジェクトを定義したもの、「インスタンス」=クラスをインスタンス化したもの、なんだなーと思っておけば大丈夫。</p> <h1 id="オブジェクトを使っていく流れ"><a href="#%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%84%E3%81%8F%E6%B5%81%E3%82%8C">オブジェクトを使っていく流れ</a></h1> <p>オブジェクトは定義して呼び出すのが基本で、こういう流れになる。</p> <p><strong>【定義する】</strong></p> <p>空っぽのオブジェクトを用意する。<br /> ⇓<br /> オブジェクトの中に変数や関数を入れていく。<br /> ⇓<br /> オブジェクトが定義できた!(オブジェクト完成!)</p> <p><strong>【呼び出す】</strong></p> <p>定義されたオブジェクトを読み込む。<br /> ⇓<br /> オブジェクトから変数や関数を引っ張りだして使う。</p> <h1 id="オブジェクトの中身を呼び出すには"><a href="#%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E4%B8%AD%E8%BA%AB%E3%82%92%E5%91%BC%E3%81%B3%E5%87%BA%E3%81%99%E3%81%AB%E3%81%AF">オブジェクトの中身を呼び出すには</a></h1> <p>オブジェクトは、中身の呼び出し方を考えてから定義することが多いので、先に呼び出し方を確認しておこう。</p> <h2 id="例"><a href="#%E4%BE%8B">例</a></h2> <p>例として、引数に与えた文字をそのまま表示するユーザー定義関数<code>foo()</code>を用意した。</p> <pre><code class="php">function foo($str) { echo $str; } </code></pre> <p>この関数を、どんな名前でもいいが、ここでは<code>MyProject</code>という名前のオブジェクトに入れることにする。(「オブジェクトを定義するには」で手順は後述)<br /> 早速オブジェクト<code>MyProject</code>から<code>foo()</code>を呼び出してみる。</p> <pre><code class="php">// 事前にオブジェクトが定義されているとする $obj = new MyProject; $obj->foo('おはよう!'); // おはよう!が表示される </code></pre> <p>最初の行を見てほしい。<br /> <code>$obj</code>という変数の中に、オブジェクトが代入されている。</p> <p>そう、オブジェクトは<code>new 〇〇</code>という書式で、変数に一度代入することで使えるようになる。これを「インスタンス化」と呼び、このとき、変数は「オブジェクト型」という状態になっている。</p> <p>つまり、上記の例ではオブジェクト型の変数<code>$obj</code>に、事前に定義したオブジェクト<code>MyProject</code>の中身がそのまま入っているのだ。</p> <p><a href="https://crieit.now.sh/upload_images/f37dd84b36bcbac2749ed7d9d5d7127f5d7d037044f18.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/f37dd84b36bcbac2749ed7d9d5d7127f5d7d037044f18.png?mw=700" alt="image.png" /></a></p> <p>この代入された変数のことを「インスタンス」という。インスタンス=オブジェクト型の変数のことである。</p> <p>続きを見てほしい。<br /> <code>$obj->foo('おはよう!')</code>とある。これは、オブジェクトのインスタンスから<code>foo()</code>というユーザー定義関数を呼び出している。</p> <p><a href="https://crieit.now.sh/upload_images/f37dd84b36bcbac2749ed7d9d5d7127f5d7d072b8b744.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/f37dd84b36bcbac2749ed7d9d5d7127f5d7d072b8b744.png?mw=700" alt="image.png" /></a></p> <p>このとき、普通の関数とは呼び出し方が異なるので、この関数のことを特別に「メソッド」と呼ぶ。</p> <p>やたら用語ばかり出てきて申し訳ない。先ほどのコードはこういうことである。<br /> <a href="https://crieit.now.sh/upload_images/f37dd84b36bcbac2749ed7d9d5d7127f5d7e3c4152609.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/f37dd84b36bcbac2749ed7d9d5d7127f5d7e3c4152609.png?mw=700" alt="image.png" /></a></p> <h1 id="オブジェクトを定義するには"><a href="#%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E5%AE%9A%E7%BE%A9%E3%81%99%E3%82%8B%E3%81%AB%E3%81%AF">オブジェクトを定義するには</a></h1> <p>さて、ここで先ほどの例で使ったオブジェクト<code>MyProject</code>を定義する手順だ。</p> <p>最初に空っぽのオブジェクトを用意する。こう書く。</p> <pre><code class="php">class MyProject { } </code></pre> <p>この中にさっき作ったユーザー定義関数<code>foo()</code>を入れてみると…</p> <pre><code class="php">class MyProject { function foo($str) { echo $str; } } </code></pre> <p>オブジェクトが完成!<br /> これだけ?これだけ!<br /> これで、<code>MyProject</code>というオブジェクトの中に<code>foo()</code>というメソッドが定義されたことになる。</p> <p>さっき呼び出しの部分で書いたものと組み合わせてみよう。</p> <pre><code class="php">class MyProject { function foo($str) { echo $str; } } $obj = new MyProject; $obj->foo('おはよう!'); // おはよう!が表示される </code></pre> <p>これで一段落。これが理解できたあなたは、とても基本的なクラス構文をマスターしたことになる。</p> <h1 id="オブジェクトにしかできないこと…プロパティ"><a href="#%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AB%E3%81%97%E3%81%8B%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%84%E3%81%93%E3%81%A8%E2%80%A6%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3">オブジェクトにしかできないこと…プロパティ</a></h1> <p>ここまで来た人は疑問に思うかもしれない。<br /> 「関数をメソッドにする意味って何なの?」「それ、ユーザー定義関数で良くね?」と。</p> <p>ここからは、オブジェクト指向だからできること、プロパティの仕組みを説明する。</p> <p>「オブジェクトって何?」で説明した図を思い出してほしい。</p> <p><a href="https://crieit.now.sh/upload_images/f37dd84b36bcbac2749ed7d9d5d7127f5d7ceecf2278b.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/f37dd84b36bcbac2749ed7d9d5d7127f5d7ceecf2278b.png?mw=700" alt="image.png" /></a></p> <p>そう、梱包されてるのは関数(メソッド)だけじゃなかった。変数もいた。<br /> このオブジェクトの中で定義された変数のことを、「プロパティ」と呼ぶ。</p> <p>プロパティの基本的な定義の仕方と呼び出し方は次の通り。</p> <pre><code class="php">class MyProject { public $text = 'おはよう!'; // これがプロパティ } $obj = new MyProject; echo $obj->text; // プロパティの呼び出し </code></pre> <p>メソッドと似ていることがわかるだろうか?<br /> そう、プロパティとメソッドは扱い方がとてもよく似ている。</p> <p>今回は、最初に少し見慣れないキーワード「public」について説明しよう。<br /> これは、メソッド(関数)やプロパティ(変数)のアクセス権を示している。<br /> PHPではpublic, protected, privateの3つがある。</p> <div class="table-responsive"><table> <thead> <tr> <th>アクセス権</th> <th>範囲</th> </tr> </thead> <tbody> <tr> <td>public</td> <td>オブジェクトの外部から呼び出せる。継承されたオブジェクトから呼び出せる。オブジェクト内のメソッドから呼び出せる。</td> </tr> <tr> <td>protected</td> <td>オブジェクトの外部から呼び出せない。継承されたオブジェクトから呼び出せる。オブジェクト内のメソッドから呼び出せる。</td> </tr> <tr> <td>private</td> <td>オブジェクトの外部から呼び出せない。継承されたオブジェクトから呼び出せない。オブジェクト内のメソッドから呼び出せる。</td> </tr> </tbody> </table></div> <p>プロパティではこれらのアクセス権を必ず明示する必要がある。メソッドでは、省略した場合publicとしてみなされる。<br /> つまり、前項の例で作ったメソッドは、publicを省略していたということだ。省略できるのだが、アクセス権はちゃんと書いておいたほうが喜ばれるだろう。</p> <p>さて、プロパティで注意してほしいのは、メソッドの外でないと宣言できないということだ。<br /> 例えば、以下のようなことはできない。</p> <pre><code class="php">class MyProject { // publicなメソッドを宣言 public function foo() { public $text = 'おはよう!'; // メソッド内なのでエラー echo $text; } } $obj = new MyProject; echo $obj->foo(); </code></pre> <p>これは、ユーザー定義関数の特性上、その中で使われる変数はローカルスコープでないといけないからだ。<br /> もしメソッドの中でプロパティを使いたいのなら、<code>$obj->text</code>みたいな感じで変数を呼び出さなければならない。だが、自分のオブジェクトの中で自分のオブジェクトを指定するのもおかしいよな…ということで、擬似変数<code>$this</code>が登場する。</p> <pre><code class="php">class MyProject { public $text = 'おはよう!'; public function foo() { echo $this->text; // $thisは自分自身を表す } } $obj = new MyProject; echo $obj->foo(); // おはよう! </code></pre> <p><code>$this</code>を使うと、プロパティを活かして、例えばこんなことができる。</p> <h2 id="カートに商品を追加する例"><a href="#%E3%82%AB%E3%83%BC%E3%83%88%E3%81%AB%E5%95%86%E5%93%81%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B%E4%BE%8B">カートに商品を追加する例</a></h2> <pre><code class="php">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個 </code></pre> <p>合計金額を表示するメソッドも作れる。</p> <pre><code class="php">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円 </code></pre> <p>オブジェクト指向の便利さはここからだ。上の例の後半だけ次のように書き換える。</p> <pre><code class="php">$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円 </code></pre> <p>インスタンス<code>$cart1</code>, <code>$cart2</code>が別々のものとして計算されている。つまり、2つのオブジェクトは独立して動作しているのだ。</p> <p>これがインスタンス化の最大のメリットである。もしこの処理をユーザー定義関数で無理やり実現しようとすると、それぞれの処理が複雑に干渉してしまい、たちまち地獄が訪れるだろう。</p> <p>処理をいい感じに独立させることは、プログラミングをする上でとても大切だ。また、プログラムに将来への拡張性があることも、どれほど大切かわかる人は多いだろう。</p> <p>オブジェクト指向はこれを実現するためにある。<br /> インスタンス化で複数の機能をいい感じに独立させられる。またクラスの中にメソッドを追加しても他の処理に影響を与えないため、将来への拡張性も確保できる。</p> <h1 id="おわりに"><a href="#%E3%81%8A%E3%82%8F%E3%82%8A%E3%81%AB">おわりに</a></h1> <p>オブジェクト指向はプログラマの「あったらいいな」が詰め込まれているため、機能が多すぎて面食らってしまいやすいが、本質的な部分はとても簡単なものである。<br /> 継承や無名関数、コンストラクタなど、面白い機能が他にもたくさんあるので、興味があるものから一つずつ調べてみるのがいいだろう。</p> <h1 id="PHPマニュアルおすすめ部分"><a href="#PHP%E3%83%9E%E3%83%8B%E3%83%A5%E3%82%A2%E3%83%AB%E3%81%8A%E3%81%99%E3%81%99%E3%82%81%E9%83%A8%E5%88%86">PHPマニュアルおすすめ部分</a></h1> <p><a target="_blank" rel="nofollow noopener" href="https://www.php.net/manual/ja/language.oop5.basic.php">PHP: クラスの基礎</a></p> <p><a target="_blank" rel="nofollow noopener" href="https://www.php.net/manual/ja/language.oop5.properties.php">PHP: プロパティ</a></p> <p><a target="_blank" rel="nofollow noopener" href="https://www.php.net/manual/ja/language.oop5.visibility.php">PHP: アクセス権</a></p> <p><a target="_blank" rel="nofollow noopener" href="https://www.php.net/manual/ja/functions.anonymous.php">PHP: 無名関数</a></p> ウラル