質問をすることでしか得られない、回答やアドバイスがある。

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

ただいまの
回答率

90.50%

  • PHP

    20749questions

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

  • オブジェクト指向

    292questions

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

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

解決済

回答 8

投稿

  • 評価
  • クリップ 7
  • VIEW 4,863

kou0179

score 36

こんにちは。

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

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

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

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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 8

checkベストアンサー

+8

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

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

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

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/05/19 12:43

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

    キャンセル

+5

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


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

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

◆最初のコード

ここで、

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

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

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

function getUserInfoFromCSV($id){
    //CSVファイルから読み取っているつもり
    return (object)array('name'=> 'hoge', 'age'=> 18, 'age_shared'=> false);
}
function renderNameWithAge($user){
    return sprintf("%s(%s)", $user->name, ($user->age_shared ? $user->age : '--'));
}
function renderUserById($id){
    $user = getUserInfoFromCSV($id);
    return renderNameWithAge($user);
}

print renderUserById('admin');

オブジェクト指向的仕様

interface IUserInfoRepository{
    public function getUserInfo($id);
}
class CsvRepository implements IUserInfoRepository{
    public function getUserInfo($id){
        //CSVファイルから読み取っているつもり
        return (object)array('name'=> 'hoge', 'age'=> 18, 'age_shared'=> false);
    }
}

interface IRenderer{
    public function doRender();
}
interface IUserRenderer extends IRenderer{
    public function setUser($user);
}
class NameWithAge implements IUserRenderer{
    private $_user;
    public function setUser($user){
        $this->_user = $user;
    }
    public function doRender(){
        return sprintf("%s(%s)", $this->_user->name, ($this->_user->age_shared ? $this->_user->age : '--'));
    }
}
class RenderUserById implements IRenderer{
    private $_repository;
    private $_renderer;
    private $_id;
    function __construct(IUserInfoRepository $repository, IUserRenderer $renderer, $id){
        $this->_repository = $repository;
        $this->_renderer = $renderer;
        $this->_id = $id;
    }
    public function doRender(){
        $user = $this->_repository->getUserInfo($this->_id);
        $this->_renderer->setUser($user);
        return $this->_renderer->doRender();
    }
}

class RendererFactory{
    public function createByIdRenderer($id){
        return new RenderUserById(new CsvRepository(), new NameWithAge(), $id);
    }
}

$factory = new RendererFactory();

$renderer = $factory->createByIdRenderer('admin');
print $renderer->doRender();

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

◆機能拡張が発生

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

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

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

function getUserInfoFromCSV($id){
    //CSVファイルから読み取っているつもり
    return (object)array('name'=> 'hoge', 'age'=> 18, 'age_shared'=> false);
}
function renderNameWithAge($user){
    return renderScoped($user, true);
}
function renderNameWithAgeScoped($user, $is_public){
    if($is_public){
        return sprintf("%s(%s)", $user->name, ($user->age_shared ? $user->age : '--'));
    }else{
        return sprintf("%s(%s)", $user->name, $user->age);
    }
}
function renderUserById($id){
    return renderUser($id, true);
}
function renderUser($id, $is_public){
    $user = getUserInfoFromCSV($id);
    return renderNameWithAgeScoped($user, $is_public);
}

print renderUserById('admin');
print renderUser('admin', false);


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

interface IUserInfoRepository{
    public function getUserInfo($id);
}
class CsvRepository implements IUserInfoRepository{
    public function getUserInfo($id){
        //CSVファイルから読み取っているつもり
        return (object)array('name'=> 'hoge', 'age'=> 18, 'age_shared'=> false);
    }
}

interface IRenderer{
    public function doRender();
}
interface IUserRenderer extends IRenderer{
    public function setUser($user);
}
abstract class SingleUserRenderer implements IUserRenderer{
    private $_user;
    public function setUser($user){
        $this->_user = $user;
    }
    public function doRender(){
        return $this->_doRender($this->_user);
    }
    abstract protected function _doRender($user);
}
class PublicNameWithAge extends SingleUserRenderer{
    protected function _doRender($user){
        return sprintf("%s(%s)", $user->name, ($user->age_shared ? $user->age : '--'));
    }
}
class NameWithAge extends PublicNameWithAge{}
class PrivateNameWithAge extends SingleUserRenderer{
    protected function _doRender($user){
        return sprintf("%s(%d)", $user->name, $user->age);
    }
}
class RenderUserById implements IRenderer{
    private $_repository;
    private $_renderer;
    private $_id;
    function __construct(IUserInfoRepository $repository, IUserRenderer $renderer, $id){
        $this->_repository = $repository;
        $this->_renderer = $renderer;
        $this->_id = $id;
    }
    public function doRender(){
        $user = $this->_repository->getUserInfo($this->_id);
        $this->_renderer->setUser($user);
        return $this->_renderer->doRender();
    }
}

class RendererFactory{
    public function createByIdRenderer($id){
        return $this->createPublicRenderer($id);
    }
    public function createPublicRenderer($id){
        return new RenderUserById(new CsvRepository(), new PublicNameWithAge(), $id);
    }
    public function createPrivateRenderer($id){
        return new RenderUserById(new CsvRepository(), new PrivateNameWithAge(), $id);
    }
}

$factory = new RendererFactory();

$renderer = $factory->createByIdRenderer('admin');
print $renderer->doRender();

$renderer = $factory->createPrivateRenderer('admin');
print $renderer->doRender();

◆ここで、修正内容を比較してみます

今回の修正は要するに、

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

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

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

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

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

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

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

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

◆まとめてみます

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

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

◆補足:NGパターン

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

  • 特定の実装に依存する必要はないのに、特定の実装に依存している。
  • 機能を使用するだけなのに、機能の生成にまで手を出している。
class RenderUserById implements IRenderer{
    private $_repository;
    private $_renderer;
    private $_id;
    function __construct($id){
        $this->_repository = new CsvRepository(); //このへんがNG
        $this->_renderer = new NameWithAge(); //このへんがNG
        $this->_id = $id;
    }
    public function doRender(){
        $user = $this->_repository->getUserInfo($this->_id);
        $this->_renderer->setUser($user);
        return $this->_renderer->doRender();
    }
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+3

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/05/19 12:45

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

    キャンセル

0

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/05/19 12:38

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

    回答、ありがとうございました。

    キャンセル

0

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

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/05/19 12:46

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

    キャンセル

0

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/05/19 12:48

    なるほど、、、まあそこまで時間の必要な処理があるならPHPで書くなよ!ってなっちゃいそうですが(笑)

    回答ありがとうございました。

    キャンセル

0

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/05/19 12:50

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

    キャンセル

0

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

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 90.50%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • PHP

    20749questions

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

  • オブジェクト指向

    292questions

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