• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:(クラス関連)staticキーワードの利用価値が分かりません。)

クラス関連でのstaticキーワードの利用価値とは?

このQ&Aのポイント
  • PHP4または5の環境でのクラスの扱い方について勉強中ですが、staticキーワードの利用価値が分かりません。
  • インスタンス化とは異なる静的メンバと静的メソッドの考え方や利用方法が分からないです。
  • staticキーワードについての疑問を持っています。staticマスターのアドバイスをお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.1

<?php class Data{ protected static $singleton = null; protected $data = array(); protected function __construct(){ } public static function getInstance(){ if(self::$singleton === null){ self::$singleton = new Data(); } return self::$singleton; } public function set($name, $value){ $this->data[$name] = $value; } public function get($name){ if(isset($this->data[$name])) return $this->data[$name]; return null; } } function output_hoge(){ $data = Data::getInstance(); return $data->get('hoge'); } class Moge{ protected $d = null; public function __construct(){ $this->d = Data::getInstance(); } public function outputHoge(){ return $this->d->get('hoge'); } } ?> <?php $data = Data::getInstance(); $data->set('hoge', 'テスト'); echo output_hoge(); $moge = new Moge(); echo $moge->outputHoge(); ?> 上記を実行するとブラウザ上に「テストテスト」と出力されます。 これは、デザインパターンの一つで「Singletonパターン」というものです。 このパターンはstaticキーワードを使わないと行えない動作です。 それで、このプログラムは下記のような流れです。 ---------------------------- Dataクラスという単純に文字列を格納するだけのクラスに'hoge'というキーに'テスト'という文字を格納し、 それぞれ、 output_hogeというユーザ関数で呼び出しecho、 MogeクラスのoutputHogeメソッドで呼び出しecho、 を行う。 ---------------------------- 次に、上記を改変して、「Signetonパターン(及びstaticキーワード)を使わない」で、 output_hoge関数と、MogeクラスのoutputHogeメソッドで、 Dataクラスというデータをためるクラスに保存した文字を出力する方法を考えてみてください。 ※なお、今回の「テスト」という文字は、定数値やメンバ変数に固定で格納される値ではなくて、 ユーザやその他(FormでPOSTされたりとか)で動的に挿入された値と考えてください。

march4
質問者

お礼

すごく難解な問題をありがとうございます。 頑張って、このシングルトンを倒してみたいと思います。(笑) 最近、GOFのデザインパターンをかじり、シングルトンさんを知りました。ただ、詳細については全く分かっていません。 さて、まず数点、確認させて頂きたいことがあります。 --- return null; } } function output_hoge(){ --- 上記は頂いたコードの一部分です。 2,3行目に「 } 」が2つありますが、 これらのうち、1つが、どこに対応するものなのか分からなかったので、 1つ多目に入っちゃってるのかなと、解釈して読み進めました。 (私の勘違いでしたら、ごめんなさい。) それと、function output_hoge()の、アクセス権は public と解釈して良いでしょうか。 このメソッドだけアクセス権が未設定でしたので、念のため、お聞きしておこうと思います。 さて、本題に戻りますが、 staticを使用しないと行えない動作というのが、 まず存在することに驚きました。 ・それは本当なのだろうか。 ・その動作には、価値はあるのだろうか。 ・私に、それらは理解できるのだろうか(笑) という疑問が、まず頭に浮かびました。 クラスについて、少しずつ分かってきましたので、 時間は掛かるかもしれませんが、頑張ってみます!

march4
質問者

補足

こんにちは! まだ回答には至っていませんが、 頂いたコードを私なりに解釈し、それをコードではなく日本語の文章で 書いてみたいと思います。 正直、コードの流れを理解することで精一杯だったものですから、 まずは、こんな所からのスタートとなります(苦笑)。 細かくは説明できませんが、要所をおさえて、コードの流れを私なりに 解説してみます。 >protected function __construct() まず、ここで、コンストラクタのアクセス権をprotectedにしているので、 このコンストラクタを持つクラス「class Data」は、 クラス外部からインスタンス化できない、という風に理解しました。 ここ、間違っていますかね? もし間違っていないとすると、 この気づきは、私にとって、とても大きな意味を持ちます。 (変なことを言っていたらすみません。) 逆に言うと、間違っている場合、この後の説明(シナリオ)は、 かなり意味の薄いものとなります(苦笑)。 で、話を戻しまして、 この、クラス外部からインスタンス化できないクラスを、 どうにかしてインスタンス化したい(その理由があるから)、 そんな時に役立ってくれるのが、 public static function getInstance()ですね? ただ、このメソッドはstaticなので、 クラス外部から呼び出す場合には、「::」の記号を使って呼び出します。 このgetInstance()は、呼び出されると、 クラス内部から、クラス「class Data」をインスタンス化する機能を果たします。 そして、return self::$singleton; のように、インスタンスをreturnします。 なお、protected static $singleton ということなので、 アクセス権上、 クラス外部から$singletonへアクセスすることは常にできない、 ということになります。 getInstance()にアクセスすることで、 $singletonにアクセスすることになるので、直接$singletonにアクセスする意味 はないと理解できます。 ちなみに、この$singletonの取り得る値は、 ・null ・クラス「class Data」のインスタンス のどちらかとなります。 次に、 public function set public function get これらは、一般的にアクセサメソッドと呼ばれていたりするものですよね。 そんな名前はさて置き、 これらは、どちらもpublicなので、クラス外部から呼び出せますが、 staticファンクションではないので、これらを呼び出すのには、 これらを包含するクラス「class Data」のインスタンス化が大前提となります。 このクラス「class Data」で、最終的にしたいことは、 引数として値をsetして、さらに、getして取り出す、 ということなので(勝手な解釈はどんどん続きます。笑)、 それらを可能にするための大前提である、 クラス「class Data」のインスタンス化はとても重要です。 そのインスタンス化を、これほどまでに、 しにくくしていることに、何らかの理由があるのでしょうね。 このあたりに、シングルトンパターンの真骨頂があるのでしょうか。 で、話を戻しまして、 こうして、 インスタンス化に成功し、 getファンクションにより値をリターンすることができるようになると、 クラス外部から、自由にset、getできるようになります。 そして、それをユーザ関数にまとめて、 クラス外部での運用のために簡易的に作られたのが、 function output_hoge()なのでしょう。 これをユーザ関数にしておくかどうかについては、 このシングルトンパターンの話とは無関係だと思っていますので、 ここはさらっと流します。 次に、クラス「class Moge」の登場です。 public function __construct() つまり、「class Moge」のコンストラクタはpublicであるので、 この「class Moge」は、クラス外部からインスタンス化することが可能です。 そして、インスタンス化と同時に、 自身(「class Moge」)の中で設定しているプロパティ「protected $d」に、 「class Data」のインスタンスを代入する、ということをしています。 これが、「class Moge」のコンストラクタです。 そして、public function outputHoge()で、 「class Data」のgetファンクションを利用しています。 setファンクションについては、この「class Moge」の中では扱われていません。 これはつまり、「class Moge」には、get機能のみ与え、set機能は与えていない、 ということを意味するのだと思います。 もっと言うと、「class Moge」は、すでにどこかでsetされた値を、 $moge->outputHoge()のカタチで呼び出すためのクラスなのでしょう。 (NO.2の補足へと続く→) ※まだまだ続きます(苦笑)

その他の回答 (6)

回答No.7

loop_check関数に関しては、 staticな変数で、値を保持しているので、 最初のwhile文を抜けても、ループして足された値が残っているので、 次にさらにwhile文で利用すると、その足された値が保持されている変数にさらに加算してしまうため、そのままでは、二度目以降のwhile文でまずい挙動を起こすということです。 >グローバル変数にしたら、そういうことになりますよね。 >staticだと、そういうことにはならなかったんでしたっけ。笑 この件に関しましては、混乱を招く記述で申し訳ないです。 globalな空間で$dataにgetInstanceしているので、以降の記述で変数がバッティングする可能性がありますね。 他の説明も含めていったことで若干矛盾が生じてしまいました。 言い訳させてもらえるなら、今回の例題として記述したものは、変数のバッティング云々の話というよりも、Singletonパターンを使うことで、別の場所でsetした値を、別のスコープを持つ領域でも、拾ってくることが出来る、ということを説明したかったのでした。 ■静的メソッドの件 >以上のどちらの方法でもアクセスできることを参考書を読み、知りました。 >ただ、どちらもアクセスの可否で言えば、「可」なのでしょうけど、 >その動作ははたして完全に等価なのでしょうか? 静的メソッドを使う場合とインスタンスを生成してメソッドを利用するという件に関しては、一度「new Hoge()」などとインスタンスを生成するのにはそれなりにコストがかかります。 例えばメンバ変数にまったくアクセスすることがない、ある一つの系統のライブラリの塊のようなクラスを作成した場合に関して、 <?php class Util{ public static function p($value){ return '<p>'.$value.'</p>'; } public static function div($value){ return '<div>'.$value.'</div>'; } } ?> 見て分かるとおり、単純に、与えられた$valueに対して、HTMLタグを付加してreturnするだけですね。 これは、メンバ変数にまったくアクセスすることなく、ただ与えられた変数値に対して、処理をしてやって返すだけですので、いちいちインスタンスを生成するコストを使わずとも、 Util::p('hoge'); Util::div('moge'); とアクセスしてやれば利用できます。 staticキーワードを消して、 $util = new Util(); $util->p('hoge'); $util->div('moge'); とやって使ってやってもいいですが、このクラスに関しては、上記の通り、メンバ変数を使わないので、ただインスタンスを生成するコストを消費するだけです。 このような例ですと、ユーザ関数を使えばいいんじゃないかと思うと思いますが、後はどのように機能を盛り込むのかとか、どういう設計をするかによって、使う使わないは決まってくると思います。 ■globalキーワードとstaticキーワード(というかSingleton) コレも上で書いたように、どのように設計するかにより、 例えば完全なオブジェクト指向を目指して設計するなら、外部にglobalな変数を置いてソレを操作するようにしないと思います。その際は、クラス内のどこかでSingletonにてオブジェクトを生成することもあると思います。 また、アプリケーションの作成ではなくて、汎用的に使えるライブラリ群を作成する場合の話ですが、 あるクラスからあるクラスを呼ぶようなライブラリだったとしてソレの読み出し方を、外だしの変数にインスタンスを格納してglobalで呼ぶというような作り方をしてしまうと、いつか使用方法を誤り、変数のバッティングが起こることもあるかもしれません。 以上、私の能力では文章でこれ以上ピンと来る説明は出来そうもなく、質問を重ねるごとに混乱が生じる可能性が高そうです。 というよりも、既にそうなっているようですね・・・・ 後は、一つ、自身のサイトなりWebアプリケーションなりを作成していくに当たり、「staticキーワードはこういうときに使うのか!」とひらめいてください。百聞は一見にしかず、です。

march4
質問者

補足

返事が遅くなりまして、すみません。 今回も大変丁寧に回答して頂きまして、とても参考になりました。 >後はどのように機能を盛り込むのかとか、どういう設計をするかによって、使う使わないは決まってくると思います。 >「staticキーワードはこういうときに使うのか!」とひらめいてください。百聞は一見にしかず そうですね! hogehoge78さんに教わったことをもとに、これからしばらくは、 実際に手を動かし、試行錯誤しながらstaticやclassの便利な使い方について、勉強していこうと思います。 次に質問をする際には、より限定的に、質問内容を絞り込んで、質問をさせて頂こうと思います。 まだまだ質問内容が漠としていると思いますので。 (ざっくりな質問に回答下さいまして、本当にありがとうございました。最初から最後までお付き合い下さいましたのはhogehoge78さんだけでした。笑) こんな私ですが、おかげさまで分からないことが、 かなり絞り込めました。 また分からないことがありましたら、色々教えて下さい。^^ 今回も、どうもありがとうございました。

回答No.6

>サイトを自分1人で運用するのであれば、その場合には、 >スクリプトの仕様が分かっている自分だけがコーディングするわけですから、 >そのコーディングにおけるシバリは緩めにしておいても問題は起こりにくいですが、 >複数人でコーディングをする場合には、static等を使用し、 >コーディングにシバリを与え、問題が起こりにくくした方が良い、という考え方ですよね。 スミマセン、私も少し誤解してました。実際にはstaticをつけないと、エラーが出てしまうようです。 ↓↓↓ static メソッドは、オブジェクトのインスタンスを生成せずに コールすることができます。疑似変数 $this は、 static として宣言されたメソッドの内部から利用することはできません。 static プロパティは、矢印演算子 -> によりオブジェクトからアクセス することはできません。 static でないメソッドを静的にコールすると、E_STRICT レベルの 警告が発生します。 E_STRICTは、「error_reporting(E_ALL);」とかphp.iniのエラーレベルの記述を変更すると出てくるエラーで、 例えば、 <?php $a = $_POST['hoge']; ?> とスクリプト中にあった場合、$_POSTがPOSTされてなかった場合、未定義の変数となり、エラーがはかれます。 実際には、 <?php $a = null; if(isset($_POST['hoge'])){ $a = $_POST['hoge']; } ?> と書かなければならないと。 E_STRICTの警告に関して問題がないのであれば、仰られるとおり、パターン0とパターン1は等価です。 >「staticを使わなくてもできるけれども、使った方がより楽」という感じでしょうかね。 ともいえるし、逆にglobalキーワード使って、オブジェクトを外だしにしてやったほうが楽な場合もあるかもしれませんね。 <?php $DB = new Database(); function hogehoge(){ global $DB; $DB->query('mogemoge'); } ?> とか。ただ、この方法はグローバルな空間に変数を増やすので、変数のバッティングを引き起こす危険性が増すので シングルトンを引いたほうが良いかもしれません。 loop_check関数に関しては、その理解でOKです。 これは、どちらかといえば、デバッグ用のコードで一行挿入するだけで とりあえず凄い回数ループしてたら止められるというものです。 (ただ、static変数引いてるので、スクリプト中に一つしか使えないですね・・・)

march4
質問者

お礼

補足です。 >static プロパティは、矢印演算子 -> によりオブジェクトからアクセス することはできません。 静的なメソッドの作法について、ちょっと私は誤解していました。 静的な「プロパティ」では、アクセスする際に必ず「::」を使うかと思いますが、 静的な「メソッド」の場合、アクセスする際に必ずしも「::」を使わなくてもよい、ということを確認しました。 ---------------------------- $obj = new Hoge; //クラスHogeのインスタンス作成 //クラスHoge内の静的なメソッドへアクセスを試みる //アクセス方法1 $obj->staticMethod();//スタティックメソッドに「->」を使ってアクセス //アクセス方法2 Hoge::staticMethod();//スタティックメソッドに「::」を使ってアクセス ---------------------------- 以上のどちらの方法でもアクセスできることを参考書を読み、知りました。 ただ、どちらもアクセスの可否で言えば、「可」なのでしょうけど、 その動作ははたして完全に等価なのでしょうか? >static でないメソッドを静的にコールすると、E_STRICT レベルの 警告が発生します。 このあたりのことについても、参考書には書かれていて、 static でないメソッドを静的にコールすること(つまり、「クラス名::メソッド名」のような書き方) は可能だけれども、そのメソッド内で$thisが使われているとエラーが出る、 という書かれ方でした。 なので、$thisさえ使われていなければ、エラーは出ないのかな? なんて思いましたが(E_STRICT レベルの 警告は発生しそうでしょうか?)、 staticでないメソッドを静的にコールすることを私はしようとは思わないので、 正直、この辺りの知識は「+α」な知識として横に置いておきます。笑 /*-------------------- ◆こちらの質疑応答を初めて読まれる方々へ 本質問においては、 staticの作法というより、機能的意味や利用価値といった所にスポットを当てて、 staticについて質問をさせて頂いております。 ----------------------*/

march4
質問者

補足

何度も、しつこく食いさがってすみません。 どうしても staticマスターになりたくて…。(なんだそれ。笑) >static メソッドは、オブジェクトのインスタンスを生成せずに コールすることができます。 >疑似変数 $this は、 static として宣言されたメソッドの内部から利用することはできません。 記述の仕方ごとゴッソリ変えないとエラーが出る、ということですよね。 それは大丈夫です。笑 staticメソッドの中で$thisが使えないとか、呼び出しで「::」を使わないとイケナイとか、 そういった書き方の所をしっかり押さえた上で、 「意味的に」staticを、「not static」の状態に 変えることで(つまり、エラーの出ない書き方をした上で)、 パターン0とパターン1の、「唯一無二のインスタンス生成の可否」に関する違い、 についてお聞きしたつもりですので、ご安心下さい。 >E_STRICTは、「error_reporting(E_ALL);」とか php.iniのエラーレベルの記述を変更すると出てくるエラーで、 よく理解されていますね。 hogehoge78さん、すごすぎです。笑 エラー出力レベルについては、その厳しさの度合いで何段階かあるな、 くらいにしか私は把握していません。 ましてや、そのレベルの違いなんて、もう全く分かりません。笑 >E_STRICTの警告に関して問題がないのであれば、仰られるとおり、 >パターン0とパターン1は等価です。 たぶん、私が気になっていたことは、こちらのコメントで解決されたと思います。 「私も少し誤解してました。」とhogehoge78さんに勘違いさせてしまうくらい、 私が初歩的な書き方をしてしまっていて、すみません。笑 staticを取り去るだけで、そのままclass Data は動くのだと私が誤解していると、 そう考えて下さったんですよね?笑 大丈夫です。その辺は、しっかり押さえています。笑 (なんか、さらに勘違いしていたらごめんなさい。爆) >ともいえるし、逆にglobalキーワード使って、オブジェクトを外だしにしてやったほうが >楽な場合もあるかもしれませんね。 なるほど、オブジェクトを静的変数(static使用)ではなく、 グローバル変数(global使用)にして処理する方が楽な場合もあるかもしれない、 というわけですね。 ここ、大事な所のような気がするのですが、ピンと来なくて困ってます。苦笑 シングルトンパターンの例で、 <?php $data = Data::getInstance(); $data->set('hoge', 'テスト'); echo output_hoge(); $moge = new Moge(); echo $moge->outputHoge(); ?> このような記述がありました。 ここでは、グローバルな感じで、setし、getしています。 つまり、グローバル変数を扱うがごとく、 static由来の変数やメソッドを使用していますよね。 生成時はstaticを使っているけれども、 呼び出し後の振る舞いは、もう静的変数ではなく、 これはグローバル変数になっている、 という考え方が正しいのでしょうか。 正直、ちょっと混乱してます。苦笑 >ただ、この方法はグローバルな空間に変数を増やすので、 >変数のバッティングを引き起こす危険性が増す グローバル変数にしたら、そういうことになりますよね。 staticだと、そういうことにはならなかったんでしたっけ。笑 あれー、大混乱。爆 ↓↓↓ staticだと、 ローカル関数内部でのみ、その値を書き換え可能かつ値の保持ができるのであって、 関数外部で、もし同じ名前の変数があったとしても、その値に影響を及ぼすことはない ということでしたよね? だから、staticを利用した場合、変数のバッティングも起こらない(起こりにくい)という 理解で良いのかな。。 ブツブツ。。もごもご。。 >(ただ、static変数引いてるので、スクリプト中に一つしか使えないですね・・・) 複数の関数内でloop_check()関数を呼び出すと、 それぞれでの処理結果が、合成されてしまう、ということでしょうか?! これはつまり、変数のバッティング?? あれ、スクリプト中に1つというのは、1つのユーザ関数内で1つということでしょうか。 1ファイルに1つということではないですよね? 1スクリプトというのを、1ファイルではなく、1ユーザ関数と捉えるべき、 ということですよね? 1ユーザ関数という解釈ですと、上記のお話は納得がいき、問題ないのですが、 1ファイルという解釈が正しいとなると、 もう、お手上げです。笑 staticって、なんだかすごく分かりにくい概念だと私は思うのですが、 皆さん、すんなり理解できてしまっているのでしょうかね。 そのへんも不思議に思っていたりします。笑

回答No.5

お久しぶりです。 お元気そうで何よりです。 >「static $a =0;」という最初の宣言と定義は最初だけ読み込まれ、 >それ以降はスルーされる、 >というように私は理解し、次のステップへと進みました。笑 その理解で問題ないです。 http://jp2.php.net/manual/ja/language.variables.scope.php このURLはPHPの公式マニュアルですが、同じこと書いてあります。 公式マニュアルの言語リファレンスは割りと重要なことが書いてありますので 道に迷ったら立ち戻ってみてください。 >以下に、新たに分かったことを殴り書きしてみます。 私の記述したクラスの理解については問題なく、よく理解出来ております。 書いた甲斐がありました(笑) >「public static function getInstance()」ここで、 >staticを取り去ったら、やっぱりダメなのでしょうかね? >$singletonがstaticでさえあれば、それで、事が足りたりはしないのだろうか、なんて考えてみましたが、 >それだと、別々のインスタンスが生成されてしまうのでしょうか? >と、多少ぎこちない面も、まだあったりします。苦笑 staticを取り去っても問題なく動きます。 http://jp2.php.net/manual/ja/language.oop5.static.php こちらに記載の通り、結局そのメソッドに対してシバリを与えるということなので、 むしろ自分が使用するクラスで、ルールが確定しているのであれば、 全てのメンバ変数をpublicにして全てのメソッドもpublicにしたっていいです。 単純に今後のためにどういう機能なのか、が分かるように、それぞれのメンバに対して 意図しないアクセスをさせない為に、記述しておく、ぐらいに思っておいたほうが判りやすいかもしれませんね。 >ある1つのクラスに対し、複数のインスタンスを作らせないことに意味がある場合に、 >このパターンは役に立つのでしょうね。 >ただ、それがどういう時なのかは、私にはまだよく分かっていませんが。苦笑 コレについては、例えばデータベースにアクセスするためのクラスを用意したとします。 その場合に、Aクラス、Bクラス、C関数のそれぞれで平行してデータベースアクセスクラスを使いたい場合があるかもしれません。 シングルトンを使わない場合毎回newすることにしたとしたら、毎回違うリソースが引かれてしまいます。 後は、クラスではなくて、ユーザ関数内で利用する場合、 function loop_check($max){ static $i = 0; if($i <= $max){ $i++; }else{ exit('制限回数を超えました。'); } } こんな感じのものを作っておくと、例えば、while文でループをさせたときに、 記述が間違えてて、とんでもない永久ループになる前に制限回数で止めることが出来ます。 while(true){ //なにやら処理 loop_check(1000); //1000回ループしたら強制的に止める }

march4
質問者

お礼

(...つづき↓) >while(true){ >//なにやら処理 >loop_check(1000); //1000回ループしたら強制的に止める >} これは、最初、「なんだ??」と思いましたが、 理解できると、とても便利なものであることが分かりますね。 「//なにやら処理」の部分が無限ループを起こさなければ、 loop_check()での処理は活かされないが、 万が一、「//なにやら処理」の部分で無限ループが起こってしまった場合には、 loop_check()での処理により、 whileから抜け出すことや、スクリプト自体を終了させたりすることができる、 というような仕組みなわけですよね。 「保険」のような機能と考えれば良いでしょうか。 無限ループが完全に起こらないという確証がない場合に、 このloop_check()なるものを仕込んでおけば、 万が一、無限ループが発生しても、強制終了をさせられ、 異常事態から回避できますからね。 私は、こういうテクニックをほとんど知らないので、とても勉強になります。

march4
質問者

補足

7月中に締め切れず、申し訳ありません。 hogehoge78さんもお元気そうで、私も嬉しいです。 >PHPの公式マニュアル はい! 組み込み関数の仕様をちゃちゃっと確認するくらいでしか利用していませんでしたが、 今後は、もっと積極的に利用していきたいと思います。 >書いた甲斐がありました(笑) このお言葉、とても嬉しかったです。笑 >staticを取り去っても問題なく動きます。 >それぞれのメンバに対して意図しないアクセスをさせない為に、記述しておく、 >ぐらいに思っておいたほうが判りやすいかもしれませんね。 言わんとすることはよく分かります。 サイトを自分1人で運用するのであれば、その場合には、 スクリプトの仕様が分かっている自分だけがコーディングするわけですから、 そのコーディングにおけるシバリは緩めにしておいても問題は起こりにくいですが、 複数人でコーディングをする場合には、static等を使用し、 コーディングにシバリを与え、問題が起こりにくくした方が良い、という考え方ですよね。 と、ここで、とても細かい話になるかもしれませんが、 hogehoge78さんに書いて頂いたclass Data において、 メンバ変数:$singleton メンバ関数:function getInstance() この二つの組み合わせ方によって、 class Dataの動作がどう変わるのか、これが気になっていました。 (以下の4パターンの違いは、staticキーワードの有無だけです。) ◆デフォルト(パターン0)---メンバ変数・メンバ関数ともにstatic 「public static function getInstance()」 「protected static $singleton」 ◆パターン1---メンバ変数だけstatic 「public function getInstance()」 「protected static $singleton」 ◆パターン2---メンバ関数だけstatic 「public static function getInstance()」 「protected $singleton」 ◆パターン3---メンバ変数・メンバ関数ともにstaticではない 「public function getInstance()」 「protected $singleton」 ※パターン3へいくほど、シバリは緩めになっていると思います。 パターン0~4まで、 もちろん、厳密には、それぞれ異なる動作をするものであることは分かりますが、 「唯一無二のインスタンス」を生成するという機能にのみ着目し、 その機能の有無(達成度)において、上記4パターンを比較した場合、 その機能においては、パターン0とパターン1は等価なのでは? と思ったわけです。 前回の補足で、 「public static function getInstance()」ここで、 staticを取り去ったら、やっぱりダメなのでしょうかね? と書いたのは、そのような意味だったのですが、分かりにくくかったですね。 すみません。苦笑 ちょっと言い方を変えて、 唯一無二のインスタンスを生成するクラス、という機能にのみ着目した場合には、 上記のパターン0とパターン1は同じ動作をするのではないか、と思ったわけです。 でも、たぶん、違うんだろうな、という風にも思っています。(どっちだ。笑) パターン2や3だと、もうこれらは、メンバ変数が static ではないので、 「唯一無二のインスタンス」というコンセプトからはハズレテしまっていると思います。 ただ、パターン1は、なんとか「唯一無二のインスタンス」という機能を維持できているように、 私には思えてしまっているのですが、やはりそんなことは、ないのでしょうか。 私は、「ファンクションがstatic」という考え方が、 いまいちよく分かっていないのでしょうね。苦笑 ファンクションがstaticかどうかに関係なく、 その中に登場する、リターン値 $singleton が static でさえあれば、 「唯一無二のインスタンス」という機能は達成し得るのでは? と思っているわけです。 分かりにくく、また、書き方がくどくてすみません。 この微妙なニュアンスを分かって頂けると、とても嬉しいです。笑 >Aクラス、Bクラス、C関数のそれぞれで平行してデータベースアクセスクラスを使いたい場合 >シングルトンを使わない場合毎回newすることにしたとしたら、毎回違うリソースが引かれてしまいます。 これ、とてもよく分かりました。 と、同時に、さすがだなぁ、と感心してしまいました。笑 この例で、シングルトンの価値がハッキリと見えてきました。 別々に処理はするけれども、処理の結果は皆で共有したいという場合に便利なわけですよね。 「staticを使わなくてもできるけれども、使った方がより楽」という感じでしょうかね。 また、毎回違ったリソースが引かれると、それだけメモリを食うことにもなるでしょうから、 staticを使うメリットは大きいと感じました。 (つづく...↓)

回答No.4

>つまり、ここまで分かっていながら、その利用価値に気づけない >というのは、もはや、static以前(つまり、頭脳)の問題である。 そんなことはなくて、staticキーワードの代替となる書き方はいくらでもあるので、「わざわざstaticを使わなくても」という感想が出てくるのは当然として、そのように書きました。 (今回の問題に関しても、staticを使わないで同じような動作をさせる回答も存在しています。) >そもそも、その程度にしかstaticキーワードというのは役に立たない 、もしくは、重箱の隅をつつくような利用法でしか一般的に使われない ということなのかな、と思いました。 そんなことはなくて、チラっと見かけた程度なので、ここでは説明しませんが、CakePHPなどのフレームワークでは便利な使い方をしていました。 Data::getInstanceに関しましては、 staticなメンバ変数$singletonに対して、インスタンスを代入しています。 そのインスタンスが代入されているself::$singletonをreturnしているため、Dataのインスタンスの参照を外の変数などに代入しているわけですが、staticなメンバ変数であるため、どこから呼んでも唯一無二のインスタンスが返せるというところになります。

march4
質問者

お礼

お返事が遅くなりまして、すみません。 >「わざわざstaticを使わなくても」という感想が出てくるのは当然 そういうことだったんですね! >CakePHPなどのフレームワークでは便利な使い方をしていました。 もう少し腕を上げないと、staticを便利に使うことはできなそうだなと感じました。 よって、しばらく、staticを勉強してきます。 クラスとは関係なく、staticというのがどういう意味を持つものなのか、 私はよく分かっていないようなので。 >staticなメンバ変数であるため、どこから呼んでも唯一無二のインスタンスが返せる おそらく、ここが私にとっては大きなポイントなのだと思います。 「どこから呼んでも唯一無二」 という使い方ができるのがstaticの特徴なんですよね。 ユーザ関数内でstatic変数が宣言された場合、 その変数は、ローカル変数ではなくグローバル変数のように 振る舞うようですが、それは、ユーザ関数内でglobal宣言するのとは また違うようですし、、、この辺りがモヤモヤしているので、 スッキリさせてから、またこちらの回答を続けたいと思います! ということで、数日、お時間を下さい。 今月中に締め切った方が良さそうな気がするので、 月末までにはなんとかしたいと思います。

march4
質問者

補足

遅くなりましたが、復活しました。 staticの使い方、静的変数の意味を学んできました。 staticの説明を読むと、大抵、 static $a =0; というような宣言と定義が、ある関数内で、まず初めに登場し、 さらにこの関数を複数回繰り返す処理をして、 static宣言された変数(静的変数)の値の変化を示すようなことをしてくれます。 が、どうも、「static $a =0;」が毎回繰り返される所で、 私は理解に苦しんでいました。 「二度目以降の関数アクセスでは、 $a = 0; の部分は無視されるのだろうか。。。」 「通常であれば、初期化されてしまうよな。。。?」 「二度目以降というのは、どうやって判断されるのだろうか。。。 単純に、初期値と異なる値が入っていたら、二度目以降と解釈される、というだけなのか」 「そういった疑問に納得のいく回答が、探してもなかなか見つからないのも、また不思議だ。。」 という感じで、フラフラしていましたが、 「static $a =0;」という最初の宣言と定義は最初だけ読み込まれ、 それ以降はスルーされる、 というように私は理解し、次のステップへと進みました。笑 次のステップとして、 改めて、この理解をもとに、もう一度シングルトンさんを読み返してみました。 以下に、新たに分かったことを殴り書きしてみます。 --- $singletonは、staticなメンバ変数であり、 かつ、 この$singletonにアクセスする方法は、 function getInstance()を通過する方法、これ1つのみであり、 このfunction getInstance()では、 (条件分岐1) $singletonの値がnullなら、 新しく$singletonにクラスDataのインスタンスを作り、それを返す、 (条件分岐2) $singletonの値が既に何かで埋まっていれば、 その埋まっている値(過去に作られた「クラスDataのインスタンス」)を返す、 という処理をする、と私は理解しました。 ここで、注目すべきは(2)で、 通常、アクセスの度に関数内にある各変数は初期化されるにも関わらず、 「$singletonの値が既に何かで埋まっていれば」という条件分岐が用意されている背景には、 「$singletonをstatic宣言したこと」があるわけですよね。 ここで、 定義通り、関数を抜けた後も、値を保持し続ける変数が静的変数なのだな、というのを強く意識しました。 よって、どこからアクセスしても、唯一無二のインスタンスが呼び出せるシングルトンパターンの仕組みについて理解することができたと思います。 ある1つのクラスに対し、複数のインスタンスを作らせないことに意味がある場合に、 このパターンは役に立つのでしょうね。 ただ、それがどういう時なのかは、私にはまだよく分かっていませんが。苦笑 イメージ的には、 output_hoge関数、 MogeクラスのoutputHogeメソッド、 それぞれから、「あるクラスの共通のインスタンス」を操作可能にする仕組み なわけですよね。 これは、 複数人で1つの大きな模型を作るときに、 A君が足を完成させ、B君が腕を、C君が胴体を、 という感じで、 複数のルートから、1つの物体にアクセスし、変化を与える作業(変化は保持される)と、 似ているなと思いました。 一方、これとは別に、 別々のインスタンスが生成可能な場合というのは、 この模型製作の例で例えるなら、A、B、C、それぞれが別の模型(つまり異なる3体)を それぞれ別々に組み立てる作業と同じなのではないかと思いました。 ここまで考えた上で、全体をもう一度俯瞰してみました所、 新たに疑問が生じてしまいました。苦笑 それは、 「public static function getInstance()」ここで、 staticを取り去ったら、やっぱりダメなのでしょうかね? $singletonがstaticでさえあれば、それで、事が足りたりはしないのだろうか、なんて考えてみましたが、 それだと、別々のインスタンスが生成されてしまうのでしょうか? と、多少ぎこちない面も、まだあったりします。苦笑

回答No.3

補足欄に記述されたプログラムの流れで問題ないです。 ■Singletonパターンに関して >インスタンスを複数作らせない、 >また、もし仮に、複数インスタンス化しても、 >それらはすべて同一のモノとして($obj1===$obj2のように)作られる、 >という所にある、なんて思っていたりするのですが、 ここまで理解していてかつstaticキーワードに意味が見出せないというのであれば、今回の回答内容はあまり意味を成さないかもしれません。 ■なぜ私がSingletonの例を出したのか staticキーワードを使っていて「それが一体、なんなのょ」といった疑問に対する回答で、最も適当であるものを探した結果です。 それ以外即席でstaticキーワードを使った例を考えようかとも思ったのですが、それこそ「それって何の意味が?」というものしか思いつかなかったので、有名なパターンの紹介を兼ねて、これを例として出しました。 ■ユーザ関数output_hogeとMogeクラス この二つの例を出した意図に関してですが、 グローバルな空間で、setした「テスト」という文字を、普通に記述したのでは拾えない空間として、二つの空間を記述しました。 この二つの空間に一箇所で定義したset('hoge', 'テスト')をSingletonパターンを使わないで簡単に拾うことが出来るかどうか、試していただきたい、というのが趣旨です。

march4
質問者

補足

>補足欄に記述されたプログラムの流れで問題ないです。 本当ですか、うれしい。笑 ということは、 下記の、 --------------- >protected function __construct() まず、ここで、コンストラクタのアクセス権をprotectedにしているので、 このコンストラクタを持つクラス「class Data」は、 クラス外部からインスタンス化できない、という風に理解しました。 ここ、間違っていますかね? もし間違っていないとすると、 この気づきは、私にとって、とても大きな意味を持ちます。 --------------- この部分は、間違っていないわけですね! いやはや、大きな前進です。 ただ、このhogehoge78さんからの最新のアドバイスを読む前に、 本屋さんで『PHPマスターブック』なる本を立ち読みしてきたのですが、 そこには、なんと、 「function__construct」はpublicでないとイケマセン。 protected,privateの設定はできません。 と書かれているではありませんか。 ホゲーっと声が出そうになりましたが、なんとか抑えました。 で、それを読み、その場で、ここでの質疑応答を思い出し、 「私が補足で書いたclass Dataの解説は、しょっぱなから間違っていたんだな、、だめだこりゃ。」 と思って帰宅し、そして、今、こちらの最新のアドバイスを読み、 またしても、 ホゲーっと声が出そうになりました。 どっちが本当なんだろう。笑 と思ってしまいますが、他の本を読むと、 必ずしも、__constructにprotectedやprivateを設定できないと、 書かれているわけではないので、 おそらく、本屋で立ち読みした本の記述に、 問題があるのではないかと、今は思っています。 その本の中では、 「設定できません」というような書き方がされていたかと思うのですが、 これは正確な言い方ではないのでしょうね。 このアクセス権に関する疑問は、実はインターフェースの所でも、 私は抱いていて、、、(と、これは別の機会にしますね。笑) >ここまで理解していてかつstaticキーワードに意味が見出せない というのであれば、今回の回答内容はあまり意味を成さないかも あらら。笑 つまり、ここまで分かっていながら、その利用価値に気づけない というのは、もはや、static以前(つまり、頭脳)の問題である。 と言われている気がして、とてもショックでした(笑)。 >それこそ「それって何の意味が?」というものしか思いつかなかったので そもそも、その程度にしかstaticキーワードというのは役に立たない 、もしくは、重箱の隅をつつくような利用法でしか一般的に使われない ということなのかな、と思いました。 ただ、Singletonパターンとして使われているstaticに関しては、 使える人には、とても意義のあるstaticの使用法なんでしょうね。 (私もそうでありたいのですが…。) 実際、どういう時にシングルトンさんが活躍するのか、 具体例が全く思いつかないのが悲しい(苦笑)。 >グローバルな空間で、setした「テスト」という文字を、普通に記述したのでは拾えない空間として このあたり、私が一番つまづいている所だと思いました。 通常、グローバル空間でsetされた値を、 関数の中やクラスの中、つまりローカル空間で受け取ることはできないんですものね? それなのに、それが出来ちゃってますよーー。 という所に、本来ならば驚くべき所を、私は驚かずにいたのは、 それはただただ私に、そのことを意識するだけの力が 無かったというだけのことです(苦笑)。 で、話を戻しまして、 一箇所で定義したset('hoge', 'テスト')を Singletonパターンを使わないで、 この二つの空間内で簡単に拾うことが出来るかどうか これを考えればいいわけですよね。 NO.2の回答の中にありました、 「output_hoge関数でも、別で作成したMogeクラスのoutputHogeメソッドでも、単一のDataクラスが扱えるよ」 の、「単一のDataクラスが扱えるよ」の部分。 ここがポイントのような気がしてきました。 私自身、 >インスタンスを複数作らせない、 >また、もし仮に、複数インスタンス化しても、 >それらはすべて同一のモノとして($obj1===$obj2のように)作られる、 のような、分かった風なことを書きましたが、 複数のインスタンスを作っても、同一のモノとして・・ という所の意義、凄さが分かっていなかったりします。 通常、ごくごく普通のクラス class Myclass に対し、 $obj1= new Myclass; $obj2= new Myclass; とすると、$obj1と$obj2は、別物(異なる2つのインスタンス) ということになりますよね。 しかし、$data = Data::getInstance(); の場合には、 むにゃむにゃ・・(よく分からなくなっている。苦笑)・・・。

回答No.2

分かりづらくてごめんなさい。 return null; } } この部分、これでDataクラスが終わります。 「output_hoge」は、ただのユーザ関数で、クラスメソッドではありません。 output_hoge関数の下に、さらに別のMogeクラスが存在しています。 これは、「output_hoge関数でも、別で作成したMogeクラスのoutputHogeメソッドでも、単一のDataクラスが扱えるよ」 ということがいいたかったので、ただのユーザ関数がまぎれてます。 スクリプト作成時にいつもタブでインデントを行っているために、投稿したら全部タブが除去されてしまって読みづらかったですね。 とりあえずそこにあるのは、 ・Dataクラス ・output_hoge関数 ・Mogeクラス のクラス2つに関数1つです。 とりあえずシングルトンにこだわらず、一番下に記述させて頂きました要件に満たすものを作成してみてください。 もしかしたらstaticキーワードが便利であることが分かるかもしれません。

march4
質問者

お礼

補足、ありがとうございます。 >「output_hoge」は、ただのユーザ関数 あ~、それが分かると、そう見えてくるものですね。(笑) すみません、眼力が無くて。^^; >スクリプト作成時にいつもタブでインデントを行っているため 書いて頂いたコードはDream Weaverにて、 タブでインデントを行って、読ませて頂いています! >とりあえずシングルトンにこだわらず はい!やってみます。 >もしかしたらstaticキーワードが便利であることが分かるかもしれません。 だといいのですが。。。(苦笑) hogehoge78さんは、その利便性について、 既に理解されているのでしょうね~。 では、しばらく考えます! 毎回、ありがとうございます。

march4
質問者

補足

(No.1の補足 の続き) 以上をまとめると、 「class Data」に、引数にて値を渡すカタチで、 値をset、getすることができ、 その結果、クラス外部からその値を呼び出せるようになります。 ただし、「class Data」をインスタンス化するのは、staticでのみ可能で、 その方法はやや特殊です。 クラス外部から、「class Data」に値をsetしたり、また、getしたりするのは、 一度、「class Data」のインスタンス化に成功したならば、 クラス外部から、 $data->set('hoge', 'テスト'); $data->get('hoge'); のようにして、行えます。 あえて、function output_hoge()のようなものを作らずとも、 上記処理は行えますが、便宜的に、ここではこのようなユーザ関数が 作られています。 で、「class Moge」についてなのですが、 なぜ、これをクラスにする必要があるのか、正直よく分かりませんでした。 これもfunction output_hoge()のように、クラスメソッドではなく、 単なるユーザ関数でも良いのではないか、と感じてしまいました。 (この点が理解できていない私は、正直、問題の核心部分を理解できていないような気がして、不安でたまりません。苦笑) シングルトンパターンの真価は、 インスタンスを複数作らせない、 また、もし仮に、複数インスタンス化しても、 それらはすべて同一のモノとして($obj1===$obj2のように)作られる、 という所にある、なんて思っていたりするのですが、 アドバイス内容に対して的外れな、 もしくは、 シングルトンの理解の仕方において間違ったこと を言っていたら、すみません(苦笑)。 その場合には適宜、ご指摘下さい。 と、ここまで書いて、 その上で、 >上記を改変して、 >「Signetonパターン(及びstaticキーワード)を使わない」で、 >output_hoge関数と、MogeクラスのoutputHogeメソッドで、 >Dataクラスというデータをためるクラスに保存した文字を出力する方法 >を考えてみてください。 ということを考えようとしています。 output_hoge関数と、MogeクラスのoutputHogeメソッドは、 改変せず、そのままにしておき、 その改変されていない、output_hoge関数と、MogeクラスのoutputHogeメソッドでも、 同様に機能させることができるような、 そんな、新しい「class Data」を、 「Signetonパターン(及びstaticキーワード)を使わない」で、 作る方法を考えてみてほしい、 ということなのですよね? 色々と知識がないため、達人の方々のように感覚的にお話を理解することができず、 その結果、達人ならば必要としない、「考え方の場合分け」が必要以上に多く 私の頭の中には発生するため、 このような、より一意性を高めた言い方で聞き直してしまっていますが、 どうか、ご容赦下さい。 なお、「私側からのメッセージ」を送信する欄 をすべて使い切ってしまったので、 申し訳ありませんが、新たな回答として「hoge」とでも打って頂けると、助かります(笑)。 それでは、上記理解のもと、新「class Data」を考えてみることに致します。 (上で書いた「考え方の基盤」自体が間違いだらけだと、ちょっと困りますけどね。苦笑)

関連するQ&A

専門家に質問してみよう