🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

オブジェクト指向

オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。

Q&A

解決済

8回答

24712閲覧

PHPでのクラスの必要性が分からない。

kou0179

総合スコア304

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

オブジェクト指向

オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。

7グッド

13クリップ

投稿2016/05/17 22:54

こんにちは。

初心者プログラマーです。
最近PHPで簡単なウェブサービスを作っているのですが、PHPでのクラスの必要性がイマイチ理解できません。(今は小規模な物であり、尚且つ基本的なところをやりたいと言ったところで、生PHPで開発しています。)

オブジェクト指向そのものは何となく理解できているのですが、クラスの有効的、効果的な使い方が分からないのです。
どっかで関数を定義して引数で投げれば良いんじゃないの? ってなってしまうんです。

でも実際にはPHPで開発されているサービス等で今やクラスを利用せずに開発されているものは無いに等しいと思います。恐らくそれだけのメリットがあるからでしょう。

具体例などと合わせて、クラスの有用性をご教授頂ければ幸いでございます。

true, tatsu_10, i50, tuu_yaa, yohhoy, ink88882👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答8

0

クローズ案件ですので、完全に蛇足ですが、
オブジェクト指向のお話では、具体的なコードが欲しいといつも思っていましたので
具体的なコードを示してみました。
超長文で申し訳ありませんが、参考になれば幸いです。
(超長文になるから、具体例が無いのかな?とも思いました…)


まず、私の場合、規模よりも改修や機能拡張があり得るかどうかで判断しています。
小規模メンテ無しで作りっきりの場合は、オブジェクト指向とか考えずに作りそうです。

>どっかで関数を定義して引数で投げれば良いんじゃないの?
仰るとおり、構造化的な関数でもほとんど似たことは出来そうですが、
改修や機能拡張に対応する仕組みが、オブジェクト指向言語には仕様として豊富に存在しているので、
オブジェクト指向の方が無理なく対応できるように思えます。

###◆最初のコード

ここで、

  • CSVファイルにあるユーザのユーザ名と年齢を表示する。
  • ただし、age_shared=falseで、年齢非公開にする。

という要求があり、コードを書くことになりました。

私がやりがちな構造化的仕様

PHP

1function getUserInfoFromCSV($id){ 2 //CSVファイルから読み取っているつもり 3 return (object)array('name'=> 'hoge', 'age'=> 18, 'age_shared'=> false); 4} 5function renderNameWithAge($user){ 6 return sprintf("%s(%s)", $user->name, ($user->age_shared ? $user->age : '--')); 7} 8function renderUserById($id){ 9 $user = getUserInfoFromCSV($id); 10 return renderNameWithAge($user); 11} 12 13print renderUserById('admin');

オブジェクト指向的仕様

PHP

1interface IUserInfoRepository{ 2 public function getUserInfo($id); 3} 4class CsvRepository implements IUserInfoRepository{ 5 public function getUserInfo($id){ 6 //CSVファイルから読み取っているつもり 7 return (object)array('name'=> 'hoge', 'age'=> 18, 'age_shared'=> false); 8 } 9} 10 11interface IRenderer{ 12 public function doRender(); 13} 14interface IUserRenderer extends IRenderer{ 15 public function setUser($user); 16} 17class NameWithAge implements IUserRenderer{ 18 private $_user; 19 public function setUser($user){ 20 $this->_user = $user; 21 } 22 public function doRender(){ 23 return sprintf("%s(%s)", $this->_user->name, ($this->_user->age_shared ? $this->_user->age : '--')); 24 } 25} 26class RenderUserById implements IRenderer{ 27 private $_repository; 28 private $_renderer; 29 private $_id; 30 function __construct(IUserInfoRepository $repository, IUserRenderer $renderer, $id){ 31 $this->_repository = $repository; 32 $this->_renderer = $renderer; 33 $this->_id = $id; 34 } 35 public function doRender(){ 36 $user = $this->_repository->getUserInfo($this->_id); 37 $this->_renderer->setUser($user); 38 return $this->_renderer->doRender(); 39 } 40} 41 42class RendererFactory{ 43 public function createByIdRenderer($id){ 44 return new RenderUserById(new CsvRepository(), new NameWithAge(), $id); 45 } 46} 47 48$factory = new RendererFactory(); 49 50$renderer = $factory->createByIdRenderer('admin'); 51print $renderer->doRender();

同じような発想で組んでみましたが、明らかに構造化仕様の方がシンプルです。
オブジェクト指向仕様の方は、すでに複雑です。
構造化仕様のメリットは、このシンプルさにありそうです。

###◆機能拡張が発生
…ここで、本人向けの表示機能が追加されることになりました。
なお、今までの表示機能は一般向けだったとの事です。
本人が見るだけなので、年齢は常に公開にしたいとの要望が発生しました。
しかも、他のコードでも私の組んだコードを使用しており、
互換性維持のため現状のメソッド名・クラス名・引数を変更してはいけない、
とのお達しがありました。

リファクタリングしつつ、対応してみます。

私がやりがちな構造化的仕様 ver2

PHP

1function getUserInfoFromCSV($id){ 2 //CSVファイルから読み取っているつもり 3 return (object)array('name'=> 'hoge', 'age'=> 18, 'age_shared'=> false); 4} 5function renderNameWithAge($user){ 6 return renderScoped($user, true); 7} 8function renderNameWithAgeScoped($user, $is_public){ 9 if($is_public){ 10 return sprintf("%s(%s)", $user->name, ($user->age_shared ? $user->age : '--')); 11 }else{ 12 return sprintf("%s(%s)", $user->name, $user->age); 13 } 14} 15function renderUserById($id){ 16 return renderUser($id, true); 17} 18function renderUser($id, $is_public){ 19 $user = getUserInfoFromCSV($id); 20 return renderNameWithAgeScoped($user, $is_public); 21} 22 23print renderUserById('admin'); 24print renderUser('admin', false);

オブジェクト指向的仕様 ver2

PHP

1interface IUserInfoRepository{ 2 public function getUserInfo($id); 3} 4class CsvRepository implements IUserInfoRepository{ 5 public function getUserInfo($id){ 6 //CSVファイルから読み取っているつもり 7 return (object)array('name'=> 'hoge', 'age'=> 18, 'age_shared'=> false); 8 } 9} 10 11interface IRenderer{ 12 public function doRender(); 13} 14interface IUserRenderer extends IRenderer{ 15 public function setUser($user); 16} 17abstract class SingleUserRenderer implements IUserRenderer{ 18 private $_user; 19 public function setUser($user){ 20 $this->_user = $user; 21 } 22 public function doRender(){ 23 return $this->_doRender($this->_user); 24 } 25 abstract protected function _doRender($user); 26} 27class PublicNameWithAge extends SingleUserRenderer{ 28 protected function _doRender($user){ 29 return sprintf("%s(%s)", $user->name, ($user->age_shared ? $user->age : '--')); 30 } 31} 32class NameWithAge extends PublicNameWithAge{} 33class PrivateNameWithAge extends SingleUserRenderer{ 34 protected function _doRender($user){ 35 return sprintf("%s(%d)", $user->name, $user->age); 36 } 37} 38class RenderUserById implements IRenderer{ 39 private $_repository; 40 private $_renderer; 41 private $_id; 42 function __construct(IUserInfoRepository $repository, IUserRenderer $renderer, $id){ 43 $this->_repository = $repository; 44 $this->_renderer = $renderer; 45 $this->_id = $id; 46 } 47 public function doRender(){ 48 $user = $this->_repository->getUserInfo($this->_id); 49 $this->_renderer->setUser($user); 50 return $this->_renderer->doRender(); 51 } 52} 53 54class RendererFactory{ 55 public function createByIdRenderer($id){ 56 return $this->createPublicRenderer($id); 57 } 58 public function createPublicRenderer($id){ 59 return new RenderUserById(new CsvRepository(), new PublicNameWithAge(), $id); 60 } 61 public function createPrivateRenderer($id){ 62 return new RenderUserById(new CsvRepository(), new PrivateNameWithAge(), $id); 63 } 64} 65 66$factory = new RendererFactory(); 67 68$renderer = $factory->createByIdRenderer('admin'); 69print $renderer->doRender(); 70 71$renderer = $factory->createPrivateRenderer('admin'); 72print $renderer->doRender();

###◆ここで、修正内容を比較してみます
今回の修正は要するに、

  • ユーザ情報の新しい表示レンダラを追加する。
  • その追加されたレンダラを使えるようにする。

という感じかと思います。

しかし構造化仕様の方は、
「ユーザ情報を取得してレンダラに投げる」という関係のない機能にまで
修正が及んでいます(renderUserById→renderUser)。

一方、オブジェクト指向仕様の方(RenderUserById)は、全く変更されていません。
修正がしかるべき場所に閉じ込められています。
さらに、このクラスは「ユーザ情報を取得してレンダラに投げる」機能として再利用できています。

###◆なぜ、このような差が起きたか考えてみます

オブジェクト指向仕様の方のRenderUserByIdは、
IUserRendererという抽象的な責務に、その機能を求めています。
一方で、構造化仕様の方のrenderUserByIdの方は、
renderNameWithAgeという具体的な実装に、その機能を求めています。

この構造化仕様の方のように、具体的な実装に機能を直接求めていると、
今回の改修のように、求める実装が変更になった場合、
機能を求める側が、機能の求め方を変えなくてはなりません。

おおよそオブジェクト指向言語には、
仕様としてインターフェースのように機能を抽象的に表現する仕様があるようです。
その仕様により、素直に、具体的な実装に直接依存することを避けることができ、
具体的な実装の変更から守られます。

###◆まとめてみます
まとめてみますと、オブジェクト指向のメリットは、
変化する場所を、その(抽象的な)責務(IUserRenderer)によりカプセル化(PublicNameWithAge等)することで、
変化しない場所を、その変化から切り離し、
結果として全体を変化に強く出来ること、
なのではないでしょうか。

その上で、クラスのメリットを具体的に考えてみますと、
インターフェースとセットで用いた場合、
インターフェースは責務の表明で、クラスはその責務の実装、
という風に分担できるという感じでしょうか。

###◆補足:NGパターン
当然ながら、メリットがあるように組まないとメリットはないように思われます。
例えば、以下のように組んでしまうと、以下の2点の意味でNGと考えています。

  • 特定の実装に依存する必要はないのに、特定の実装に依存している。
  • 機能を使用するだけなのに、機能の生成にまで手を出している。

PHP

1class RenderUserById implements IRenderer{ 2 private $_repository; 3 private $_renderer; 4 private $_id; 5 function __construct($id){ 6 $this->_repository = new CsvRepository(); //このへんがNG 7 $this->_renderer = new NameWithAge(); //このへんがNG 8 $this->_id = $id; 9 } 10 public function doRender(){ 11 $user = $this->_repository->getUserInfo($this->_id); 12 $this->_renderer->setUser($user); 13 return $this->_renderer->doRender(); 14 } 15}

投稿2016/05/24 05:31

i50

総合スコア227

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

生のPHPで書いて1ファイルで済むくらいの、小規模なプログラムであれば、クラスを切り出すことへのメリットはそこまで感じられないかと思います。

ただ、プログラムの規模が大きくなってくると、規模比例よりひどい形で、プログラミングが複雑となってきます。

そのような状況下でコードとデータを「オブジェクト」という単位にまとめることで、

  • モジュールとして外部から使う場合に、「オブジェクト」単位で見られるようになるので、考えることが減る
  • オブジェクト内部の変数も、(必要とあれば)メソッドからしかアクセスできないようにすることで、不必要な依存性を作ることを避けて、クラス内部での整合性確保やクラス外とのインターフェースの簡略化に役立てる

などのメリットがあります。

もちろん、関数だけでもこのようなプログラミングは、やろうと思えばできます(C言語でそのような枠組みを組み立てる、GObjectというライブラリもあります)。とはいえ、「運用ルール」として作ったところで往々にして破られるものですので、言語側のルールとして組み立てられるクラス構造は有用です。

投稿2016/05/17 23:21

maisumakun

総合スコア145957

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kou0179

2016/05/19 03:43

なるほど、大規模な開発、チームでの開発となると活用すべきですね。 回答、ありがとうございました。
guest

0

オブジェクト指向の現実
古い記事ですが、参考になります。

ライブラリ等の再利用前提のプログラムは、オブジェクトで作られていないと使用しにくいですね。
ライブラリを使用しない、小規模開発ではあまり意味はないかもしれないです。

投稿2016/05/17 23:29

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kou0179

2016/05/19 03:45

そうですね。再利用向け等だとそっちのが良いかもです。 サイト拝見させて頂きましたが、小規模であれば余計な工数が増えてしまうので規模に合わせて考えた方が良さそうですね。 回答ありがとうございました。
guest

0

特に必要ないということであれば必要ないと思います。手段ですから。

私の場合は特に考えないでクラスを作っています。それが数十行のコードであってもです。

メソッドを切り分けるのと手間は変わらないし、小さなシステムであれば、設計を細かく考えなくてもいいので、気軽に使います。大きなシステムであれば、バグ除去のために必須です。

コーディング量は増えますが、コードの見通しが良くなり、ユニットテストがしやすいので、バグが減り開発工数が使わない場合に比べて増えるというのも感じないです。

投稿2016/05/24 06:18

iwamoto_takaaki

総合スコア2883

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

PHPは特にビルドをしないので、
実行してみるまでプログラムの入力ミスに気づけないです。
最悪なのは、実行しても気づけないケースが有るというところです。

クラス化することの意味は、IDEでコード補完してくれる所です。
これは、生産性だとか品質を考えたら、便利な機能ってだけではなく、
マストな技術だと思うのですが如何でしょう。

投稿2016/05/18 23:30

TetsujiMiwa

総合スコア1124

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kou0179

2016/05/19 03:50

そういう部分でも開発する上での効率が上がるんですね。 回答ありがとうございました。
guest

0

Webアプリにおいてクラスを使用しない場合、リクエストに対して、実行されるプロセスが一つしかないので、リクエスト解決の待ち行列ができます。
なので、速度にも関係してくることがあります。

投稿2016/05/18 01:46

koufukurairai

総合スコア64

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kou0179

2016/05/19 03:48

なるほど、、、まあそこまで時間の必要な処理があるならPHPで書くなよ!ってなっちゃいそうですが(笑) 回答ありがとうございました。
guest

0

混在して記述できる言語仕様なので、メリットはあまり感じられないかもしれません。
maisumakunさんがおっしゃっているようなメリットはあるのですが、結局必要に迫られて使って見ないとメリットを実感するのは難しいです。

私が具体的に感じているメリットは、例えば何かのフレームワークのプラグインなどを開発する場合、全ての関数にプレフィックスをつけなくても、関数名の衝突は避けやすいです。
また、特定の機能に関する処理を一箇所に集約しやすい(クラスを定義すると自然とそのように・・・)

というぐらいでしょうか。

小規模なプロジェクトではクラスを使わないといけない、というほどのメリットはないと思います。
書けば即動くのがphpの魅力でもありますし。

投稿2016/05/18 01:31

munyagu

総合スコア479

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kou0179

2016/05/19 03:46

やはり規模、再利用性の有無により使い分けるのが良さそうですね。 回答ありがとうございました。
guest

0

正直なところ同感ですな。
ただ、PHPだからそう感じるという側面はあるかも?
PHPは元々お手軽に動的なWebページを作り上げるという
目的でできたと聞いてます。時代の流れでプログラミングの
流行りものをキャッチアップしてきたようですが、
どうしても後付けで使いづらいという印象も。
たぶん他の言語を使って開発していったら
また印象が異なることになるかもしれませんねえ。:-)

投稿2016/05/17 23:12

takasima20

総合スコア7460

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kou0179

2016/05/19 03:38

ですよね。クラスを使わずとしてほとんど問題無いんです・・・  他の言語に触れてみればその穏健が分かるかもしれません。 回答、ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問