PHP4 真の OO

WBOY
WBOYオリジナル
2016-06-21 09:14:481153ブラウズ

この記事の著者である Johan Persson は、PHP の有名な JpGraph チャート ライブラリの開発者です。この記事は、PHP4 でオブジェクト指向開発を行う際に注意する必要があるいくつかの小さな問題について著者がまとめたものです。 Binzy Wu [メール: Binzy at JustDN dot COM]、レベルは限られていますが、議論を歓迎します。 2004-2-4

はじめに
この記事は、Eiffel などのより成熟した OO [1] 言語を使用したことのある人を対象としています。 、Java、C# [2] または C++()、開発中の友人 (私など) は、完全な OO 開発に PHP4 を使用する場合、多くのセマンティック トラップ [3] (セマンティック) を希望します。この記事は、他の人が私が犯した間違いを避けるのに役立ちます。

参照とコピー セマンティクス
これは、基本的に (少なくとも私にとっては) エラーの主な原因です。PHP のドキュメントでも、PHP4 は参照よりもコピー セマンティクスを使用していることがわかります。 (私がオブジェクト指向言語を知っている他の人たちと同じように) しかし、それでも最終的には細かい点で悩まされることになります

次の 2 つの部分では、コピー セマンティクスがあなたを驚かせるかもしれない 2 つの小さな例を説明します。クラス変数はクラスへのポインタではなく、実際のクラスそのものであることを常に覚えておくことが重要です [5]。ほとんどの問題は、オブジェクトにエイリアスを与える代入演算子 (=) の誤解から生じます。実際には新しいコピーです。たとえば、$myObj がクラスのインスタンスであり、それに Set() メソッドがあるとします。その場合、次のコードは C++ (または Java) プログラマが期待する

function SomeFunction($) のようには見えないかもしれません。 aObj) { $aObj->Set(10); }

SomeFunction ($myObj);



したがって、Set() メソッドと呼ばれる関数が $myObj に対して動作すると考えるのが簡単です。しかし、これは間違いです!

実際に起こることは、$myObj が元のオブジェクトの新しい同一のコピー (パラメーター $aObj) にコピーされることです。その後、Set() メソッドが呼び出されるとき、それはローカル コピーに対してのみ作用します。元のパラメータ----$myObj.

上記のさまざまな問題は、直接的または間接的(上記のような)代入操作が含まれる場合に発生します。

関数が期待どおりに動作するようにするには(そしておそらく実際に動作するでしょう) ) の場合は、次のようにメソッド宣言を変更して、オブジェクトを参照によって渡すように PHP に指示する必要があります。
Function SomeFunction(&$aObj)


もう一度試してみると、上記のコードで Set() が実行されていることがわかります。このメソッドは、アクション内に $myObj のエイリアス ---- $aObj.

を作成したため、元のパラメータに基づいて動作します。次の例のように、

参照から参照を取得します

次のコードがあるとします。
$myObject = new SomeClass();


$myRefToObject は既に参照なので、次のように記述したくなるかもしれません。
$myCopyRefToObject = $myRefToObject; えっ? $myRefToObject によって参照されるオブジェクト。オブジェクトへの参照をコピーしたい場合は、次のように記述する必要があります。
$myCopyRefToObject = &$myRefToObject;


は、C++ の例では、参照の参照と同等です。 PHP とは異なります。これは、経験豊富な C++ プログラマーがよく行う直感的な想定に反しており、これが PHP プログラムの小さなバグの原因になる可能性があります。そこから生じる問題

私は個人的に、これらのセマンティック トラップを回避する最善の方法は、オブジェクトまたはオブジェクトの割り当てを渡すために常に参照を使用することであるという結論に達しました。これにより、実行時間が向上する (データ コピーが減る) だけでなく、また、私のような老犬にとってセマンティクスがより予測しやすくなります。

コンストラクターで $this への参照を使用します

オブジェクトのコンストラクター内で、他のオブジェクトのディスカバーとして機能するオブジェクトを初期化するのが一般的なパターンです (Observer[次のコード行は例です:
class Bettery
{
function Bettery() {…};
function AddObserver($method , &$obj)
{
$this->obs[] = array($obj, &$method)
}
function Notify(){…}
}
class Display
{
function Display(&$batt )
{
$batt->AddObserver("BatteryNotify",$this) );
}
function BatteryNotify() {…}
}


ただし、次のようにオブジェクトをインスタンス化すると、これは正しく動作しません:
$myBattery = new Battery();$myDisplay = new Display($私のバッテリー);これを行う際の間違いは、new のコンストラクターで $this を使用すると同じオブジェクトが返されず、代わりに最近作成されたオブジェクトのコピーが返されることです。つまり、AddObserver() を呼び出したときに渡されたオブジェクトが同じではありません。その後、Battery クラスが (Notify メソッドを呼び出して) すべてのオブザーバーに通知しようとすると、作成した Display クラスではなく、$this で表されるクラス (つまり、作成した Display クラス) が呼び出されます。したがって、Notify() メソッドがいくつかのインスタンス変数を更新する場合、更新されるのは実際にはコピーであるため、元の Display クラスは更新されません。これが機能するためには、コンストラクターが同じオブジェクトを返すようにする必要があります。これは、$myDisplay = & new Display($myBattery); のように、Display コンストラクターに & 記号を追加することで実行できます。直接の結果として、Display クラスのクライアントは、実際、これは物議を醸す可能性のある問題を引き起こします。前述したように、基本的には安全ですが、これを無視すると、上記の例のように動作します。

それを解決するには、JpGraph で別のメソッドが使用されます。つまり、&$this によって参照される "Init()" メソッドをオブジェクトを "new" するために安全に使用できる、いわゆる 2 段階の構造を使用する必要があります。 (コンストラクター内の $this 参照がオブジェクトのコピーを返すため、これは期待どおりに動作しません)。したがって、上記の例は次のように実装されます。
$myBattery = new Battery();
$myDisplay() );
$myDisplay->Init($myBattery);


JPGraph.phpの「LinearScale」クラスなど


foreachを使用する

同様のコードで結果が異なるもう1つの問題は、「foreach」構造の問題です次の 2 つのループ構造のさまざまなバージョンを調べてください。
// バージョン 1
foreach( $this->plots as $p )
{
$p->Update();
}



/ / バージョン 2
for( $i=0; $iplots); ++$i )
{
$this->plots[$i]-> Update();
;


ここで $10 の質問 [7]: version1==version2 ですか?

驚くべき答えは次のとおりです: これは小さいですが重要な違いです。バージョン 1 では、Update() メソッドは次のコピーに対して動作します。したがって、配列内の元のオブジェクトは更新されません

バージョン 2 では、Update() メソッドは配列内の []" オブジェクトに対して期待どおりに動作します。 .

最初の部分で述べたように、これは PHP がオブジェクト インスタンスをオブジェクト参照としてではなくオブジェクト自体として処理した結果です。

翻訳注:
[1]。OO: オブジェクト指向、オブジェクト指向。
[ 2]。原文には C# はありません。すべて Binzy の個人的な好みによるものです。
[3]。この記事では Semantic を「意味論」と訳しています。ご提案があれば、Binzy までご連絡ください。 C++ には有名な「C++ Gotchas」があります。
[5]、ここのクラスはインスタンスである Instance を参照する必要があります。
[6]「デザイン パターン」を参照してください。 7 ]. 取引については、非常に興味深い小さな話があります。
ある人が馬を 60 ドルで購入し、70 ドルで売りました。その後、80 ドルで買い戻し、最終的には 90 ドルで売りました。この馬の取引では、彼は (A) 10 ドルを失い、(B) 利益は 10 ドルでした。(E) は 30 米ドルでした。これは、ミシガン大学の心理学者のメルとバークが尋ねた単純な算術問題です。その結果、大学生の 40% 未満が正しい答えを与えることができ、ほとんどの人は 10 ドルしか稼げないと考えていました。実際、これらは 2 つのトランザクションです。毎回 10 ドルの利益が得られますが、多くの人は、80 ドルで買い戻したときに 10 ドル損をしたと誤解しています。興味深いことに、同じ問題を別の言い方で言えば、ある人は白い馬を買いました。 60 ドルで購入し、70 ドルで売却しました。その後、ダークホースを 80 ドルで購入し、90 ドルで売却しました。このとき、彼は__(同じ 5 つの選択肢をリストしました)。大学生が上記の質問に答え、全員が正解しました






声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。