PHPでOOP
PHPを使ってプログラミングするとき、 プロシージャ指向(手続き型、構造化プログラミング)でもできますが、 オブジェクト指向を使った場合の恩恵を享受するために、 PHPでオブジェクト指向プログラミングの勉強をしてみましょう。 <目的> PHP5でオブジェクト指向プログラミングを行なうための知識を習得する。 (PHP4のOOPもOK、このスレが1000に行く前にPHP6が出たらPHP6のOOPもOK) <方向性> ・このスレは、プログラミング初心者、PHP初心者の勉強の場として利用することを前提にします。 ・PHPのOOPの話題に限定します。 (Ruby、Python、Javaなど他言語のOOPについては、その言語のスレッドでお願いします。) ・PHPのOOP学習に役立つ本、WEBサイトの紹介をお願いします。 <その他> ・略記は、「OO」=「オブジェクト指向」、「OOP」=「オブジェクト指向プログラミング」でお願いします。 ・質問をする人はなるべくトリップを付けましょう。 ・荒らし、煽り、叩き、気違いは無視・無干渉でお願いします。 このスレで、今日から貴方もOOP!!!\(^o^)/ [cfcontrol.php] <?php include("./cfview.php"); include("./cfmodel.php"); class CFControl{ function CFControl($form_str, $in_str){ if( ($form_str == "")or($form_str == "in") ){ $form = new CFView("index.php","in",""); $form->Write_HTML(); }elseif($form_str == "out"){ $da = new CFModel(); $dat = $da->ReadDat($in_str); $form = new CFView("index.php","out", $dat); $form->Write_HTML(); } } } ?> [cfmodel.php] <?php class CFModel{ var $m_csv_file; // コンストラクタ function CFModel(){ // 読み込むCSVファイルを指定 $this->m_csv_file = "csv.txt"; } // データを取り出す。 function ReadDat($str){ $INFILE = fopen($this->m_csv_file,"r"); $line = fgets($INFILE, 1024); fclose($INFILE); $line = $line . ", " . $str; return $line; } } ?> [cfview.php](1/2) <?php class CFView{ var $m_file; // POSTするファイル名 var $m_type; // 表示するフォームの種類。in か out var $m_line; // 表示するデータ // コンストラクタ function CFView($file, $type, $line){ $this->m_file = $file; $this->m_type = $type; $this->m_line = $line; } // private function in_html(){ echo "<html><body>"; echo '<form method="POST" action="' . $this->m_file . '">'; echo '<input type="hidden" name="form" value="out">'; echo '<input type="text" name="key"><input type="submit" value="送信">'; echo "</form></body></html>"; } [cfview.php](2/2) // private function out_html(){ echo "<html><body>"; echo '<form method="POST" action="' . $this->m_file . '">'; echo '<input type="hidden" name="form" value="in">'; echo "$this->m_line<br>"; echo '<input type="submit" value="戻る"></form></body></html>'; } // public function Write_HTML(){ if($this->m_type == "in"){ $this->in_html(); }elseif($this->m_type == "out"){ $this->out_html(); } } } ?> とりあえず、MVCに分けて枠組みを作ってみたけれど、 これをより抽象化させていって、「継承して使ってください」という 方向にするのか、それとも最初はクラスの数を増やさないように しながら簡単なアプリケーションを作る方向にするべきか。 どっちの方向に持っていったほうがいいのか迷うな。。。 ま、そんなことを考える暇があったら手を動かしてみろという 話なのかもしれないが。。 >>190 自分で考えるのも良いが、君が今やっていることを やってしまっているのが、フレームワークだ。 まず既存のフレームワークがどうなっているのか参考しろ。 俺も初心者だからこれが最善とは言い切れないけど newするときに全部引数で渡すってのはナシじゃね? 分かりやすいところだけ書き出すと [index.php] $form = new CFControll(); [cfcontrol.php] コンストラクタ() { $form_str = $_POST['form']; $in_str = $_POST['key']; if(inだったら){ $view = new CFView(); $view->m_type = 'in'; $view->Write_HTML(); } } [cfview.php] メンバ変数 var $m_file = 'index.php'; var $m_type = false; var $m_line = null; フレームワーク使ってみろっていうのは賛成 疎結合にとかDRYにっていうのがだんだんわかってきた 理解したところで戻ってきて〜の方が結果的に早そう 俺はまだ勉強中だからそこまで行ってないけど >>192 > $view = new CFView(); > $view->m_type = 'in'; これみたいに、直接メンバにアクセスするのは構造的に良くないと聞いたことが あるよ。「データをやり取りするのは、インターフェースを通じて」という原則を 守るべきだと。 そうしなければ、CFViewクラスを改変する人は、そのクラスを使っている人の コードを考慮して、メンバの値や変数名を自由に変える事が出来なくなるから。 なので、私は、コンストラクタで値を渡しても良いし、コンストラクタで値を渡して いなければ、値を渡すためのインターフェースを使って渡すようにする仕様が 適当かなと思っている。 汚染されちゃうけどコンストラクタで全部の値渡すよりはましじゃないかなあ あとコンパイルするときに全部チェックしてくれる言語とそうじゃない言語ってのもある phpなんだしゆるーくやればいいじゃん なんていうと怒られるかw 今調べて知ったのだが、オーバーロードは PHP ではできないらしい。 だったら、コンストラクタで値を渡すよりも、インターフェースで値を 設定するような仕組みになるだろうね。 コンストラクタだと、一度値を設定したら、そのオブジェクトが破棄される まで、再度設定が出来なくなるから。 メンバ変数へのアクセスはsetter/getterを使う。これは議論の余地なし。 それを用意した上でコンストラクタに引数を渡すなら渡せば良い。 複雑で多くの設定をしなきゃならない時以外、 newした直後に使える状態になっている方が使いやすい。 > $view = new CFView(); > $view->m_type = 'in'; これをセットで書かなきゃならないなら、 > $view = new CFView('in'); と書きたい。 私は>>197 さんの意見に同意だ。 「このモジュールを使う場合、このように書いてくださいね。」 というコードは、なるべく少ない方がいいからね。 なので、とりあえず設定の値はコンストラクタにいれるという 設計で書いてみた。 とりあえず、フレームワークを使ってみろという話が出ているが、 具体的にどのフレームワークを使って、どんなプログラムを書いて みたらいいのか迷うなぁ。 とりあえずはこのあたりに載ってるものの、「和モノ」あたりからかな。 http://pc11.2ch.net/test/read.cgi/php/1197383840/3 フレームワーク自体の自作の話もいくつかあるみたいだ。 ttp://codezine.jp/a/article.aspx?aid=104 viewに渡すデータはセッタで渡したくならない? あとinなのかoutなのか分岐させるとしたらそれはコントローラ側の仕事なんじゃないかなと思うんだけど違うかな >>200 > viewに渡すデータはセッタで渡したくならない? 表示させるデータはセッタがいいだろうね。 > あとinなのかoutなのか分岐させるとしたらそれはコントローラ側の > 仕事なんじゃないかなと思うんだけど違うかな >>185 のソースがそれにあたるものだと思ってたけど。 if( ($form_str == "")or($form_str == "in") ){ 省略 }elseif($form_str == "out"){ 省略 } コントローラは、POSTしてきた値を見て、必要なModelやViewを 選択し、実行する役割なので、それを実現したつもり。 厳密にMVCを分けることは出来ない場合もあるということだけど、 CFControlクラスで、CFViewを使って表示する内容までもを 指定していする処理を書いていたのは間違いかな? 検索結果の表示や、データの更新の場合は、 Control→Model→View だけど、 ボタンを押した時の画面の展開のみの場合は、 Contol→View という流れとなり、Viewオブジェクトを 生成するクラスが異なるという処理でいいのかな? 「とりあえずはフレームワークを使ってみろ」という返事がきそうだけど、 各クラスの役割は以下のような感じでいいかな? Control ・POSTでデータを受け取り、その値に不正なものが無いかをチェック。 ・変なところからのアクセスではないかをチェック。 ・$_POST["Form"]の値をみて、それに必要な画面と処理を判断する。 Model ・SQLを発行し、データを受け取る。 ・データをViewクラスに渡す。 View ・フォームを表示する。(フォームごとにクラスを分けたほうがいいのかは迷うな) ・データを1件受け取り、tableタグでレイアウトを調整し、表示する。 自分なりに調べて見つけたPHPのサンプルを使った解説ページも 読むとwebアプリについて学べるのではないかと思っている。 やることが多くなったけれど、とりあえずは以下の3本だてで 勉強してみることになるのかな。 MVCに分けて、簡単なアプリを自作する。 (ログイン、メニュー、検索条件指定、検索結果、データ編集などの画面があるもの) 和モノフレームワークを使って学ぶ。 簡単なアプリを自作する。 http://pc11.2ch.net/test/read.cgi/php/1197383840/3 サンプルで理解! フォームデータの受け渡し ttp://www.atmarkit.co.jp/flinux/rensai/mysql5_03/mysql5_03a.html ちいたんのソース見てみたけれど、 class CObject ってあって、必ずそれが継承されて作られてるよね。 これの都合って何なんだろう。(メリットは何?) javaも.NETもこういう基本クラスがあるよね。 >>208 サンクス。 でも、オーバーロードが出来ない場合は逆に足かせになる可能性もあるね。 例えば、継承されているクラスが沢山ある状況でObjectクラスに メソッドを追加する場合とか。 喋るのはコントローラとモデル コントローラとビュー 基本的にはね 少ない数のクラスを書いたり読んだりする程度であれば、すぐに分かるのだが、 フレームワークレベルのクラス構造となると、その構成が全く分からなくなって 来るんだよなぁ。何かコツのようなものはあるのかな? 処理の内容を追いかけると、次々に別のクラスに処理を渡す構造になっていて、 最後はあっけない、みたいな感じだ。 フレームワークを作る場合のクラスの設計手法を身につけるなどしないと いけないのかも。 メンバに定義はしていないけれど、メソッドではその変数をエラーが 出ないように処理が書かれているっていう書き方は多いようだ。 そうしておけば、そのフレームワークを使う人は、クラスを継承して メンバに値を代入するだけで良い。 このサイト、説明は分かるのだが、具体的に作っているコードは MVCのうちどれにあたるのかがいまいちです。 ttp://www.stackasterisk.jp/tech/php/phpMvc01_01.jsp Result.php は、Viewにあたるものという解釈でいいんですよね? PHPでMVC関連のサイトを紹介で貼っておきます。 特集:第3回 PHPを思うままに操れるようになる「MVC」と「Smarty」 (1/4) ttp://www.itmedia.co.jp/enterprise/0402/19/epn01.html PHPでね、イベントドリブンなWEBフレームワークとか自作してみるといいかも。 例えば、サブミットボタンの処理ハンドラがオーバーライドで記述可能で そこでフォーム値をモデルに渡して処理させるみたいなやつ・・ 「POST」や「GET」とかローレベルの概念は全て隠蔽されてて フレームワークにイベント発生時のロジックだけ記述して終わりみたいなの・・・ そしてPHPであれこれ試行錯誤したあと、ASP.NETとか参考にするとね PHPでOOPするバカらしさに気付くかもしれない・・・OTL ASP.NET は、ちょっとだけやってみたことあるけど、概念的に違和感が あって、やらなくなったな。 ある程度Webアプリを学んだ事のある人には便利なんだろうけれど、 初めて学ぶ人には、ドキュメントが少なすぎだし、いきなりイベントドリブンで やるのはどうかと思った。 で、まずは、Webアプリの基礎をやるという意味合いでPerlをやってみた。 で、今はPHPをやっている。PHPそのものがOOPに完全な対応をしていない ので、これで大規模なアプリを組むことも無いかなと思っている。 対応したとしても、それからノウハウが出てくるので、さらに数年先になる。 でも、学ぶ時は、既存のモジュールを使って早くやるのよりも、モジュール なしの状態で、モジュールを作ってみる方がいいので、とりあえず今は PHPでOOPです。 >>217 多分、その違和感のある概念がOOPの本質だと思うよ。 そしてその概念は、洗礼された実装に触れることでしか 身につかないとも思うんだ。 初心者こそイベントドリブンを真っ先に学習したほうがいいよ。 最終的に、理解し易く安全な実装方法に結びつくと思うからね。 PHPでOOPで実装ってケースはありだとは思うけど、 概念は別で学習した方が効率的だと思うんだ。 OOPに取り付かれているとか良くわからんw 普通にプログラミングしていると使うだろ? switchに取り付かれているとかそういうレベルに聞こえるんだが。 MVCモデルでプログラミングする場合、Model から View へ処理を渡す経緯は、 どっちが正しいのかな? ・Control クラスのメソッド内で、Model クラスと View クラスのインスタンスを生成する。 Control クラスが、Model からデータを受け取り、View クラスへデータを渡し、 描画指示を出す。 ・Model クラスのメソッド内で、View クラスのインスタンスを生成する。 Model クラスが、Viewクラスへデータを渡し、描画指示を出す。 Control クラスは、View クラスを一切操作しない。 それとも、こういうところまでは理論的には定めていないので、 ケースバイケースであり、どちらがよいというものは無いということかな? PHPでイベントドリブンですか?(・∀・) …こんなのありました。^^ PHP イベントドリブン に一致する日本語のページ 約 10,600 件 ●PRADO http://www.pradoframework.com/wiki/index.php/Ja:What_is_PRADO >PRADO はコンポーネントベースかつイベントドリブンなウェブアプリケーションを開発するためのPHP5フレームワークです。 ●S2Prado.PHP5 http://labs.s2php5.jp/s2prado.php5 http://blog.xole.net/article.php?id=553 >S2Baseの方は待望のPRADO対応。 ●Piece Framework http://trac.piece-framework.com/piece-unity/wiki/ja/Start >Piece_Unityは、Visual BasicやDelphiのようなイベントドリブンなフレームワークです。 ●Delphi for PHP http://www.codegear.com/jp/products/delphi/php http://orz.qzlab.com/yamagw/index.php?Delphi%20for%20PHP%A4%CE%BB%C8%A4%A4%CA%FD >イベントドリブンなロジックの実装が容易に実現する。 ●Pharon http://pharon.lolipop.jp/ >最大の特徴は、wizard によりイベントドリブン型のスケルトンを自動作成することです。 インターネット越しにイベント処理をさせるのが、WEBプログラミングの特徴ですね。 イベントドリブンは、PHPよりもむしろFlash/Flexとかで使われる仕組みなのでしょうか? レガシーの中心でのOOP http://kaede.to/ ~canada/doc/2005/07/06/ >Webプログラミングにおいて、ブラウザとのやり取りがレガシー(古典的)なデータ交換に過ぎず、これがWebプログラミングを難しくしている >Webは1ページごとに毎回セッションが起動し、ドキュメントを表示するとすぐ終了する。 >オブジェクト指向プログラミングにおける利点の1つであるイベントドリブンなプログラミングは不可能だ。 >何しろ1セッションに1イベントしか発生しないのだから。 >と同時にセッション状態を保存する必要も出てくる。 http://www.adobe.com/jp/devnet/flex/articles/framework_beta_print.html >インタラクティブ性に優れたイベントドリブンなインタフェイス http://www.atmarkit.co.jp/fwcr/rensai/flex203/01.html >イベント処理によってアプリケーションを構築する手法はイベント駆動型(イベントドリブン)と呼ばれます。 http://www.azul.systems-noel.jp/item_9.html >Flex2はイベントドリブンなので、ビューに起こったイベントをコントローラのリスナでキャッチするように意識すれば、MVCの分離はきれいにできるようになっています。 >なんかほんとにJavaのSwingを使ってるような気分になりますね。 http://bitmap.dyndns.org/blog/archives/001215.html >イベントドリブンモデルには、主に以下の 4 つのオブジェクトが登場する。 >>220 >・Control クラスのメソッド内で、Model クラスと View クラスのインスタンスを生成する。 こっちの方が、Controlにまとまっている分だけスッキリしており、分かりやすいコードになるんじゃないでしょうか? >>224 レスありがとうございます。 1番目の方にすると、Modelクラスから取得したデータを Viewクラスに渡すことになるので、その分余計にメモリや CPUを消費してしまうのでは、と心配になって聞いてみましたが、 考えてみると、コードの見易さなどを優先するのがOOPですので、 そちらの方がいいですね。 でも、フレームワークのソースなどを見ていると、 各クラスが、メンバに、別のクラスへのリファレンスを持ってたり するので、もっと理論に従った組み方があるのかも、と思っています。 >>184 ファイル:全部で8つ。index.phpを実行する。 抽象クラスと具象クラスに実装を分けてみました。 csv.txt(※前回と同じ) index.php cfcontrol.php アブストラクトとして実装 cfview.php cfmodel.php コンクリートとして実装 data_model.php index_view.php output_view.php [config.php] <?php // 実際の処理を行うスクリプトをインクルード include("./index_view.php"); include("./output_view.php"); include("./data_model.php"); // 最初に呼ばれるビューのプレフィックス設定 define ('INDEX_VIEW_PREFIX', "Index"); // モデルクラスのプレフィックス設定 define ('MODEL_PREFIX', "Data"); ?> [index.php] <?php include("./cfcontrol.php"); $view_key = $_POST["view_key"]; $data = $_POST["data"]; $app = new CFControl($view_key, $data); $app->Execute(); ?> [cfmodel.php] <?php class CFModel { var $file_name; // 読み込むファイル名 function CFModel() {}// コンストラクタ function Execute($param) // パブリックメソッド { return $this->_OnExecute($param); } function _OnExecute($param) // 仮想メソッド { trigger_error('オーバーライドしてね。', E_USER_ERROR); } } ?> [cfview.php] <?php class CFView { var $file_name; // POSTするファイル名 function CFView() {} // コンストラクタ function Execute($param) // パブリックメソッド { return $this->_OnExecute($param); } function _OnExecute($param) // 仮想メソッド { trigger_error('オーバーライドしてね。', E_USER_ERROR); } } ?> [cfcontrol.php] <?php include("./cfview.php"); include("./cfmodel.php"); include("./config.php"); class CFControl { var $_view_key; // 呼び出すビューのプレフィックス var $_data; // モデルに渡すデータ function CFControl($view_key, $data) // コンストラクタ { $this->_view_key = $view_key; $this->_data = $data; } function Execute() // パブリックメソッド { // モデルオブジェクト動的生成 $model_class_name = MODEL_PREFIX . 'Model'; $model = new $model_class_name(); $param = $model->Execute($this->_data); // ビューオブジェクト動的生成 $view_key = $this->_view_key; if ($view_key == "") $view_key = INDEX_VIEW_PREFIX; $view_class_name = $view_key . 'View'; $form = new $view_class_name(); $form->Execute($param); } } ?> [data_model.php] <?php class DataModel extends CFModel { function DataModel() // コンストラクタで取得先のファイル設定 { $this->file_name = 'csv.txt'; } function _OnExecute($param) // オーバーライドメソッド { $INFILE = fopen($this->file_name,"r"); $data = fgets($INFILE, 1024); fclose($INFILE); $data = $data . ", " . $param; return $data; } } ?> [index_view.php] <?php class IndexView extends CFView { function IndexView() // コンストラクタでPOST先のファイル設定 { $this->file_name = 'index.php'; } function _OnExecute($param) // オーバーライドメソッド { echo "<html><body>"; echo '<form method="POST" action="' . $this->file_name . '">'; echo '<input type="hidden" name="view_key" value="Output">'; echo '<input type="text" name="data"><input type="submit" value="送信">'; echo "</form></body></html>"; } } ?> [output_view.php] <?php class OutputView extends CFView { function OutputView() // コンストラクタでPOST先のファイル設定 { $this->file_name = 'index.php'; } function _OnExecute($param) // オーバーライドメソッド { echo "<html><body>"; echo '<form method="POST" action="' . $this->file_name . '">'; echo '<input type="hidden" name="form" value="Index">'; echo "$param<br>"; echo '<input type="submit" value="戻る"></form></body></html>'; } } ?> >>226-232 サンプルソースありがとうございます。 抽象クラスの書き方に慣れてますね。私はこのあたりを しっかりとやってなかったのでちょっと苦手です。 ま、しっかりと勉強していきたいと思います。(^^; ソースを読んでいて、1点気になったので質問をしたいのですが、 class CFView と class CFModel において、以下のように パブリックメソッドと仮想メソッドを作り、パブリックメソッドから 仮想メソッドを実行する形式にソースを書いた理由は何でしょうか? 出来ましたら、この設計にした意図を教えていただきたいと思います。 function Execute($param) // パブリックメソッド { return $this->_OnExecute($param); } function _OnExecute($param) // 仮想メソッド { trigger_error('オーバーライドしてね。', E_USER_ERROR); } >>218 レスありがとうございます。 イベントドリブンそのものは、VBでWindowsアプリを組んでやったことがあるので すぐに入れたのですが、Webアプリを作る際、イベントドリブンでしかやった事が 無いというのは致命的だと思ったので、PerlやPHPでやってみています。 (ASP.NETは、便利ではあるが、IISを使えとか、.NET Frameworkを使えとか 非常に限定される。) 構造化プログラミングで、あまり命名規則を考えずにプログラムをしていると、 グローバル変数や関数が多くなった時、その把握が出来なくなったりする わけなのですが、そういう苦労する体験をした後、OOPを習うと、その便利な部分が 見えてくるわけです。OOPは経験による結論的な理論だな、と理解できるわけです。 その理解のために、とりあえず、苦労をする方法(PHP で 0 から OOP)で やってみているのです。 今は、このように考えています。 >>233 PHP4では全てパブリックだけど例えばC#では以下の実装になるんだ public object Execute(object parpam) protected virtual object _OnExecute(object parpam) CFControlから_OnExecuteメソッドを隠蔽する意図なんだよ。 _OnExecuteはCFViewやCFModelのサブクラスにだけ見えれば十分なんだ。 >>235 暖かいですねぇ。 >>236 なるほど。ありがとう。 ソースを読んでいて気になった点がありますので、質問させていただきます。 includeの構成についてです。まず、各ファイルに書かれているincludeの部分をまとめます。 [index.php] include("./cfcontrol.php"); [cfcontrol.php] include("./cfview.php"); include("./cfmodel.php"); include("./config.php"); [config.php] // 実際の処理を行うスクリプトをインクルード include("./index_view.php"); include("./output_view.php"); include("./data_model.php"); これは、MVCフレームワークは、以下の3つのファイルであり、 [cfcontrol.php][cfmodel.php][cfview.php] それを拡張する形で、残りの6つのファイルを付け加えた形 なので、このようなincludeの構成ということでよろしいのでしょうか。 includeをばらばらとさせるよりも、以下のように整理したほうが となんとなく思ったりもしたのです。 [index.php] include("./config.php"); [config.php] include("./cfcontrol.php"); include("./cfview.php"); include("./cfmodel.php"); include("./index_view.php"); include("./output_view.php"); include("./data_model.php"); MVC?な俺にはここが一番わかりやすかった 実例コードが載ってるのがいい PHPでMVC第1回:前編 ttp://www.stackasterisk.jp/tech/php/phpMvc01_01.jsp ソースコードをちょっとだけ改変したものを作ってみた。 メモとかを残していく都合もあると思ったから、HP解説してみた。 ttp://www.geocities.jp/narutobakijp2/ 本当は、>>1 さんがソースの管理とかもしてくれたりしたら、うれしいw >>243 CFViewクラスに具体的な実装をしちゃダメなんだよ。 そもそもHTMLのフォーム処理とかは、あとでPEARとか使えばいい。 サブクラスをうまく呼び出す仕組みだけを実装していくんだ。 >>244 > サブクラスをうまく呼び出す仕組みだけを実装していくんだ。 kwsk >>246 構造化プログラミングはルーチンを呼ぶ方向で実装すると思うけど OOPではルーチンに呼ばれる方向で実装して行く感じだよ。 大枠の骨組みだけを抽象クラスで作成して、処理は具象クラスで行うんだ。 インターフェイスさえ同じならあとで個別にパーツを交換出来たりするからね。 だったら基底クラスのメソッドなんて数個で十分じゃないかと思うんだ。 やたら複雑でよくばりな機能のクラスなんて、再利用の価値がないからね。 >>247 レスありがとうございます。 > 構造化プログラミングはルーチンを呼ぶ方向で実装すると思うけど > OOPではルーチンに呼ばれる方向で実装して行く感じだよ。 私は、継承を活かした設計をした事が無かったので、ちょっと方向性を 誤ってしまったようですね。 Viewは、表示をつかさどるのだから、html表示を請け負うのでは、と 思っていたのですが、それよりも抽象的な枠組みを定義するという ことですね。 となると、html表示は(PEARを使わないのであれば、)htmlタグの 記述を行うCF_HTMLクラスを作り、Viewの具象クラス内で インスタンスを生成ということですよね? >>248 HTML処理のヘルパクラス作成はあまりOOPの勉強にならないとも思うんだ。 もう既に頭の中で実装出来ているだろうし、引数を関数で処理するだけでしょ? それよりも例えばPEARのHTML_QuickFormやテンプレートレンダラのSmartyを Viewと連携させる仕組みとかを考えたりした方がよっぽど面白いよ。 すべてをフルスクラッチするプログラミングの方向性は必ずしも得策じゃないよ 既存のライブラリやコンポーネントを上手く利用するのもOOPの要素なんだよ。 OOPで継承を用いた設計について調べてみた。(OOP理論の入門ではなく、 継承を用いた設計などが入った解説) この連載は良いかもしれない。 オブジェクト指向プログラミング超入門 .NETでオブジェクト指向プログラミングを始めよう http://www.atmarkit.co.jp/fdotnet/basics/oop_index/index.html 特に第6回は、今まで出てきていた話題だと思う。 Objectクラスで仮想メソッドToStringをもち、それから派生したクラスは、 オーバーロードをする仕組みを図説していて分かりやすい。 第6回 階層の頂点に立つクラス http://www.atmarkit.co.jp/fdotnet/basics/oop06/oop06_01.html >>250 PHPのプログラマにも非常に参考になると思いますよ。 .NETの世界はクラスベースなので初めからOOPの思考で実装します。 関数が作れないので構造化思考のVB6プログラマとか、クラスをnewせずに 引数を大量に渡すスタティックメソッドを呼んだりしてしまいます・・・ PHPはC言語での関数モジュールを呼び出す実装スタイルに近いので やはりクラスを使って構造化プログラミングをしちゃいがちですね。 普及しているPHP4がOOP対応不十分なのと、開発環境が貧弱であることも PHPでOOPがなかなか利用されない原因になってたりしますよね。 プロテクテッドメソッドの概念とかIDEがないと、なんでそうするのか なかなか理解出来ないとも思いますしね。 いくらかのデータを登録し、その内容を検索するWebシステムで使用する クラス構成で、Viewに絞った構成を考えてみた。 [View] ├[認証] ├[個人情報入力] ├[メニュー] ├[検索指定] ├[検索結果] 別の案として、[View]から[Input View]と[Output View]の 二つを継承し、さらに以下のような継承も浮かんだけれど、 継承して分ける必要性は無さそうなので、上記の方が良いように思う。 [Input View] ├[認証] ├[個人情報入力] [Output View] ├[メニュー] ├[検索指定] ├[検索結果] >>252 [View] ├[LoginView] ├[InsertView] ├[MenuView] ├[SelectView] ├[ResultView] こんな感じ? Debug用出力のメソッドをView(基底クラス)に追加するといいだろうね。 各画面([LoginView] [InsertView] [MenuView] ・・・)で エラー確認用のメソッドをオーバーロードする形で。 開発中はPOSTで受け取ったデータとかを画面上部に表示しながら動作確認する っていうのは、よくやるからね。 Objectクラスを継承する形にするのは、このスレでは共通したメソッドの 実装という理由だ。という話だったけど、リファレンス関係の処理で 便利だという話がサイトに載っているようだね。 このあたりの考え方も活かすと良いかもしれない。 ただ、PHPのOOPだとうまく実装出来ないかもしれないが。 >>207-209 >>250 Modelクラスも以下のメソッドを追加するという感じで設計すると良いのかな。 Select // データ取り出し Delete // 削除 Insert // 新規追加 Update // 既存データの更新 >>228 に載ってる既存のクラスには Execute があるけれど、 これも残しておくべきかな? 案がいくつか出てきたので、前回の駄目なソースを破棄して またソースコードをリニューアルしようかと思ったが、 いざやり始めてみると、データベースを管理する基底クラスの 設計を具体的にどうするかをきめないといけなくなり、それを どうするかで迷っている。。。 概ね以下のような感じにしたいと思ってるんだけどね。 DB格納の基底クラス:CFDB テキストファイルに保存するクラス:Text_DB extend CFDB PostgreSQLに保存するクラス:PostgreSQL_DB extend CFDB MySQLに保存するクラス:MySQL_DB extend CFDB で、この3つのいずれかのインスタンスを Model のメンバに持たせる。 現在のコード(CFModelのメンバ) var $file_name; // 読み込むファイル名 更新案のコード var $m_db; // データベースを格納。 >>255 普通は機能をメソッドに振り分けると思うけど コントローラのメソッド内で以下の様に 操作出来たら面白くない? // モデルにフォーム値を渡してデータ書き込み $insert = $this->models['InsertModel']; $insert->setItem('name', $_POST['name']); $insert->setItem('title', $_POST['title']); $insert->setItem('body', $_POST['body']); $insert->execute(); // ビュー表示用にデータをロードする $select = $this->models['SelectModel']; $data = $select->execute(); >>256 自分なりに試行錯誤で機能を実装してみるのも楽しいかもね。 誰か>>252 のアプリ作成に必要な要求仕様作ってよ。 >>257 要求仕様案 私は眼鏡屋(○○支店の店長)をやっている。 顧客の情報(名前、住所、性別、生年月日、・・・)を管理したい。 ・眼鏡を購入した顧客の情報を登録しておき、ある一定期間経つと 新しい眼鏡を購入する案内のDMを発送したい。 →条件検索をしたい。 ・登録した情報を検索し、どんなお客様かをすぐに確認出来るようにする。 →例えば、苗字を聞いて、詳細が分かるようにする。 ・眼鏡の定期的なメンテナンスで、訪問してきたら、その対応履歴を 登録しておきたい。 →眼鏡のクリーニング、シリコンパッド(鼻にあてるやつ)の交換など ・定期的なメンテナンスは無料であるので、全く店に来ない客には、 その無料案内のDMを発送したい。 →今回は、複数の店舗にまたがった処理はいらない。 ・他の社員に勝手に情報を触られないように、認証を通すようにする。 眼鏡の在庫管理は、このシステムとは別。 これは、プログラムの規模が大きくなりすぎるようであれば、 もちろん内容を変えてもいいです。「顧客の情報を登録し、 それを検索する」という流れだと、勉強用サンプルとして非常に 良いのでは、と思いました。 [データの入力例] 氏名:茂名 フリガナ:モナー 性別:男性 生年月日:H1/3/3 住所:東京都・・・ コメント:ふちなしタイプを希望している。 眼鏡購入日:2007/1/5 眼鏡の型番:2ch [2007/1/5に購入した2ch]のメンテナンス履歴 2007/2/5:クリーニング 2007/3/15:ネジの交換 2007/5/1:クリーニング、曲がったフレームの修正 顧客のデータ1件に、眼鏡の購入日がつながり、さらに、メンテナンス履歴が つながる構成になるけれど、今回は勉強用なので、「顧客のデータ1件=眼鏡の購入日1件」 としてもいいかもね。 再度購入したら、顧客の個人情報を0から入力しなおすみたいな。 View の Debug メソッドの仕様もどうするかで迷っている。 Execute($param)したあと、Debugとか、もしくは、 Debugの中でExecute($param)を呼び出す処理だと、 <html>タグのそとに確認データが出力されてしまう。 Debug モードを on にすれば、<body>タグの上部に テーブルで区切ってデータが表示されるとかかな。 だったら、メンバにDebugモードを追加かな。 という感じに考えてる。 みんなどう思う? >>258 ちょっとしたシステムじゃん・・ (´・ω・) それこそ既存のフレームワーク使用する案件だよ。 >>260 ディレクティブ付けたechoやvar_dump埋め込みで十分だと思うよ。 むしろその機能を実装するより、デバッガ環境構築した方がいい・・・ OOPに対する基本的概念への理解があまりにも無さ過ぎると思うんだ。 >>255 にしても、Executeメソッドに呼ばれる仕組み作ってんのに、 なんで新しいメソッド実装して直接呼びたがるんだろう? あれほどインタフェイスだけで実装するんだと(ry それよりコントローラの実装仕様どうするの? >>243 >ttp://www.geocities.jp/narutobakijp2/ ↓動作サンプルを設置しました。 http://ssurl.net/so2o ↓コードに関するコメントのまとめ(>>184-226 辺り) http://ssurl.net/h8bu >>262 非常に乙です。m(_ _)m >>226 だけど >>1 さんにここまでやって貰っちゃって申し訳ないし これぞOOPってサンプルを必死に実装してアップするんでしばしお待ちを・・ >>263 はやくアップしろよなw 俺がそれ見て勉強して、いつかエロイ人になったら お前を雇ってやるよ! 感謝しろ >>262 動作サンプルまでつけていただいて、ありがとうございます。 過去ログも、そのままコピペするんじゃなくて、色をつけたり 分類したりすると非常に分かり易いですね。 ShiftJISだったりとか、スペース2個というのは標準じゃないとかは 気づいてたのですが、そこまで治していただいて申し訳ないです。 >>266 別にわかんなくったって、やってけるから大丈夫。 無理に背伸びする必要は無い。 フレームワークの解説に関するサイトを見つけました。 ここで概要をつかんだ後、実際に触れてみるといいかもしれない。 ASP.NET vs. Struts フレームワーク徹底比較[前編] http://www.atmarkit.co.jp/fdotnet/special/aspstruts01/aspstruts01_04.html この文章書いてる人、ネットワーク関連の書籍でよく見かけるよね。 >>261 > OOPに対する基本的概念への理解があまりにも無さ過ぎると思うんだ。 > >>255 にしても、Executeメソッドに呼ばれる仕組み作ってんのに、 > なんで新しいメソッド実装して直接呼びたがるんだろう? > あれほどインタフェイスだけで実装するんだと(ry ちいたんのフレームワークは、Modelにinsertやdelを持ってるからそれを 参考に設計してみたんだけど。 ttp://php.cheetan.net/manual/model.php 俺はこれから勉強していくところなので理解がないのは認めるが、 このあたりはどういう見解なのかを教えて欲しい。 今回作るMVCフレームワークは、学習用なのでもっと簡潔な レベルなのを想定しているとか、ちいたん作っている人がOOPに 関する理解が無いだけだとか。 >>269 フレームワーク実装に正解も不正解も無いと思うけどね・・ 例えば ・クラスを使った構造化的メソッド呼び出し $model->insert(); $model->del(); よりも ・ポリモーフィズム $insert->execute(); $del->execute(); のほうがインターフェイスが規定されていて 簡潔で安全だと説明したかったんだよ。 insertメソッドやdelメソッドを呼ぶ文脈が規定されていたらどう? insertオブジェクトのexecuteメソッドならオブジェクトが 文脈をコントロール出来るでしょ? どうかな? 学習用だからこそ『呼ばれる仕組み』で実装しようとしているんだよ。 >>270 レスサンクス。となると、 class CInsert extend CView、class CDel extend CView、・・・ みたいな設計にするということ? ちょっと大雑把になってるけど、CInsertはこんな感じに実装するとか。 (テーブルのフィールドは、a,b,cという場合。) class CInsert extend CView{ var $field_a; var $field_b; var $field_c; function setItem($field, $data){ if($field == "a"){ $field_a = $data; } if($field == "b"){ $field_b = $data; } if($field == "c"){ $field_c = $data; } } function _OnExecute($param){ $fp = fopen($file_name,"a"); $write_line = $field_a . "," . $field_b . "," . $field_c; fwrite($fp,$write_line); fclose($fp); } } じゃ、用件仕様はこんな感じで良いのか? [認証] →・ID、パスワードにて認証 ・認証成功で[メニュー]へ移動 [メニュー] →・(新規)[個人情報入力]、[検索指定]画面へ移動するボタンがある [個人情報入力] →・名前、性別 を登録 [検索指定] →・氏名のキーワードを含む検索、性別指定が出来る。 [検索結果] →・条件に一致するデータを一覧で出す。 ・個人情報を修正したい場合は、ここから[個人情報入力]へ移動する。 ・個人情報を削除したい場合は、ここで削除ボタンを押す。 >>271 今こんな感じ。 [DataModel.php] <?php /** * データModel抽象クラスです。 */ class DataModel extends Model { # @access private var $_items; # @access public var $file_name; # @access public function setItem($key, $value) { $this->_items[$key] = $value; } # @access public function getItem($key) { return $this->_items[$key]; } } ?> [InsertModel.php] <?php /** * データ追加Model抽象クラスです。 */ class InsertModel extends DataModel { # @access sealed function & _onExecute(&$param) { return $this->_OnInsert(&$param); } # @access protected function & _onInsert(&$param) { trigger_error('オーバーライドして下さい。', E_USER_ERROR); } } [SampleInsertModel.php] <?php /** * データ追加 サンプルクラスです。 */ class SampleInsertModel extends InsertModel { # @access protected function & _onInsert(&$param) { // ここで初めてユーザ定義のメソッドを実行する。 // FWからここが呼ばれるまで待ってるのがポイント! // INSERTイベントの処理ハンドラに記述するともとれるね。 return $this->_saveData(); } /** * ユーザ定義のプライベートメソッド */ # @access private function _saveData() { // リクエストは以下のインターフェイスで取得。 $value = $this->getItem('hoge_data'); // ここで初めてHogeHogeする。 // DBオブジェクト呼ぶなりご自由に! return $data; } } ?> 細かい指摘になるけれど、継承関係の勉強中なので質問で書き込みします。 [InsertModel.php] class InsertModel extends DataModel function & _onExecute(&$param) のところは、 return $this->_OnInsert(&$param); となっているけれど、 return $this->_onInsert(&$param); が正しいという解釈で良いのですよね? >>273-275 ソースのサンプルサンクス。 イメージしてたよりも継承が多いですね。 全体ソースコードの可読性よりも、クラス単位での 再利用性を考えた場合は、このような構成になる のでしょうね。早く慣れないといけません。 まだ中身が出来ていない状況なので、修正の必要はあるだろうけど、 こんな感じでドキュメントもまとめていくと、分かりやすくなるだろうね。 ■SampleInsertModelクラス[SampleInsertModel.php] Model - DataModel - InsertModel - SampleInsertModel ◎概要 DBへのデータの記録、読み取りを行うクラス。 ◎メンバ一覧 [publicコンストラクタ] SampleInsertModel() [publicメソッド] setItem($key, $value):setter。フィールド名を指定し、データを登録する。 getItem($key):getter。フィールド名を指定し、データを取得する。 Execute($param):setItemでセットしたデータをDBに記録する。 ◎使用例 $model = new SampleInsertModel(); // クラスのインスタンスを生成する。 $model->setItem('Name', 'Tarou'); // [Name]フィールドに[Tarou]を登録する。 $model->setItem('Sex', 'man'); // [Name]フィールドに[man]を登録する。 $model->Execute(); // setItemで登録したデータをDBに書き込む。 >>263 ひとまず出来ました・・疲れました・説明は後でアップしようと思います・・ ttp://proxy.f3.ymdb.yahoofs.jp/bc/4525b767_dfac/bc/452a/PHP%a5%d5%a5%ec%a1%bc%a5%e0%a5%ef%a1%bc%a5%af.zip?BCUjxqHB4brtVvJJ >>279 乙です。じっくりソースを読んでみます。 せっかくプログラムを作っていただいたのだから、みんなでその説明文章をまとめるといいかもね。 例えば、こんな感じでhtmlで書いておいて、ファイル名をクリックすると、その詳細の説明のページに飛ぶとか。 [abstract] [controls] 空 [models] DataModel.php、DeleteModel.php、InsertModel.php、SelectModel.php、UpdateModel.php [views] HtmlQuickFormSmartyView.php、RenderView.php [controls] MainControl.php、SkeletonControl.php [data] 空 [framework] Base.php、Control.php、Model.php、View.php [lib] Request.php [models] SampleInsertModel.php、SampleSelectModel.php、SkeletonSelectModel.php [smarty] [cashe] 空 [templates] index_tmple.html、output_tmple.html [templates_c] 空 [views] HtmlQuickFormSmartyIndexView.php、HtmlQuickFormSmartyOutputView.php IndexView.php、OutputView.php、SkeletonView.php app.log、appconf.php、csv.txt、hoge.txt、index.php、Main.php、userconf.php >>281 >>279 ですがphpDocumentorで今作っているのでちょっと待っててね。 phpDocumentorにソース読み込ませて吐かせただけです。 ttp://proxy.f3.ymdb.yahoofs.jp/bc/4525b767_dfac/bc/452a/PHP_FW_DOC_01.zip?BCKv5qHBVNsncE.h フォルダ内のindex.htmlです、荒いですがご容赦を。 とりあえずトライアルなんでまだリファクタ出来そうだけど・・ [コントローラの処理] _form_onLoad _buttonHoge_onClick [モデルの処理] _onSelect _onInsert [ビューの処理] _onRender 上記のイベントハンドラにユーザ処理を記述して フレームワークに呼んでもらう構造になってます。 >>216 を実装したつもりです・・・ 有名なハリウッドの法則です。 [カプセル化]は良いとして、やはり[継承]と[ポリモーフィズム]を うまく使うのは最初難しいかもしれない・・ これらの実装もデザパタとしてライブラリ化されたものに過ぎないけどね。 read.cgi ver 07.5.5 2024/06/08 Walang Kapalit ★ | Donguri System Team 5ちゃんねる