2012-11-16 4 views
38

StackOverflowには同様の質問がいくつかあります。this questionなどがあります。メソッドパラメータをPHPの厳しい基準に違反させるのはなぜですか?

はなぜメソッドのパラメータPHPにおける厳しい基準の違反をオーバーライドしていますか?例えば :

class Foo 
{ 
    public function bar(Array $bar){} 
} 

class Baz extends Foo 
{ 
    public function bar($bar) {} 
} 

厳格な基準:次のことができ、他のOOPプログラミング言語でバズ::バーの宣言()はFoo ::バーの と互換性があります()

。なぜPHPでそれは悪いですか?

答えて

46

、オープンクローズシングル責任についてSOLIDスタンド、 、リスコフ置換、界面分離および依存性逆転。コンピュータプログラムにおいて、場合バーがfooのサブタイプである、

Liskov substitution原理の状態は、次にタイプフーの目的は、所望の特性のいずれかを変更することなく、タイプバーのオブジェクトに置き換えてもよいですそのプログラムの正確さ(正確さ、実行されたタスクなど)。

Fooメソッドをオーバーライドすると、バーでシグネチャを変更すると、実際にはにオーバーロードされます元のメソッドと新しいメソッドが異なるシグネチャで使用できるためです。 PHPは弱い型付けなので、コンパイラはあなたが実際に呼び出すメソッドを知ることができないので、これを実現することはできません。 (したがって、署名が異なる場合でも同じ名前の2つのメソッドを持つことはできません)。

リスコフSubstituition原則の違反を避けるために、厳格な標準の警告が発行されるので、プログラマの何かを伝えることにより、子クラスのメソッドシグネチャの変化に壊れるかもしれません。

+20

私はここで2つのことに同意しなければなりません: "BarがFooのサブタイプである場合、Foo型のオブジェクトはBar型のオブジェクトに置き換えられます(逆も同様)。逆の部分は成立しません。サブクラスが使用されている場所でスーパークラスを使用することは期待していません。もう一つはLSPに関するものです:もしパラメータが反動的であれば、型は尊重されます。 PHPが純粋なオブジェクト指向であれば、ArrayはObjectよりも狭いので(宣言の型がオブジェクトを意味しないと仮定して)、問題はありません。したがって、bar()の実装はBazでより一般的になり、LSPに合うようになります。 –

+1

@AndrésFortier私はFoo、Foo、Barの範囲で互換性があるべきであることを意味しました。しかし、第二の話題については、私はあなたに同意します。 Kaaseが与えた例は、Baz :: bar()が安全にFoo :: bar()を置き換えることができるため、LSPに違反しません。 – Tivie

+2

非常に良い説明、ありがとう。それでは、なぜコンストラクタでLiskovの原理を破ることができますか? –

5

パラメータを無効にすることはできますが、署名は一致する必要があります。 $barの前にArrayを入れても問題はありません。

はたとえば、あなたは追加のパラメータを追加した場合、何の問題もないでしょう、最初のパラメータは同じタイプヒンティングを持っていた提供しました。これはどの言語でもよい方法です。

+0

しかし、なぜ署名が一致する必要がありますか? Javaでは、私は署名を変更することができます –

+1

@ KaernStone、あなたもPHPですることができますが、しないでください。あなたが 'Baz'という名前の' Foo'のサブクラスを持っていて、 'Foo'のように' Baz'のインスタンスとやり取りしたいと思うコードがいくつかあります。 'Baz' **は' 'Foo''なので、修正せずにやることができます。 PHPはこのプリンシパルを壊すことができますが、そうしなければならない非常に正当な理由がない限りは、そうしないでください。 – Brad

+1

@ブラッド、ティヴィーの答えに私のコメントを参照してください。パラメータに型を入れないと、メソッドが任意のオブジェクトを取ることができるということは、それらが反変であることを意味し、したがってBaz ** is-a ** Fooです。タイピングシステムの観点からは問題はありません。してください[ここ](http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29) –

2

あなたは拡張Barで、$barの型が宣言されていない間$barは、タイプarrayであるべきことFooに宣言しているため。

これはエラーではありません。警告です。メソッド定義を元の基本クラスと互換性のあるものにする必要があります。 OOPであなたあなたは何をやっている知っていれば、しかし、安全にそれを無視することができます(あなたは何をやっている知っている場合のみをして!!!)

+0

はい、私は知っています。しかし、なぜメソッド定義を元の基本クラスと互換性を持たせるべきですか? –

+1

@ KaernStone:他のクラス/メソッド/アプリケーションは元のインターフェイスに依存しているため、変更すると互換性が損なわれる可能性があります。 –

16

私はパーティーに遅れていますが、答えは実際に実際の問題を綴るません知っています。

問題は、PHPは関数/メソッドのオーバーロードをサポートしていないです。型指定されていない言語で関数のオーバーロードをサポートすることは困難です。

ヒントが役立ちます。 PHPでは非常に限られています。理由は分かりません。たとえば、変数がintまたはBooleanであるというヒントはできませんが、配列は問題ありません。 Go figure!

他のオブジェクト指向言語では、関数のオーバーロードを使用してこれを実装しています。つまり、関数のシグネチャは明らかに異なります。

は、次のことが可能であったならば、我々は問題

もちろん
class Foo 
{ 
    public function bar(Array $bar){ 
     echo "Foo::bar"; 
    } 
} 

class Baz extends Foo 
{ 
    public function bar(int $bar) { 
     echo "Baz::bar"; 
    } 
} 


$foo = new Baz(); 
$bar = new Baz(); 
$ar = array(); 
$i = 100; 

$foo->bar($ar); 
$bar->bar((int)$i); 

would output 

Foo::bar 
Baz::bar 

それはコンストラクタに来ていないように、例えばPHP開発者は、彼らが好むと好まざるとにかかわらず、それを実装する必要が実現しました!したがって、彼らは単純にエラーを抑制するか、最初のケースでエラーを発生させません。

どちらが愚かですか。

知り合いはかつて、PHPが名前空間を実装する方法としてのみオブジェクトを実装していると言っていました。今私はそれほど重大ではないが、取られた決定のいくつかはその理論を支持する傾向がある。

コードを開発する際に常に最大限の警告がオンになっていますが、何を意味するのか、その意味が理解されていない限り、警告は出ません。個人的に私はこの警告を気にしません。私は何をしたいのか分かり、PHPはそれを正しく行えません。私はここでそれを選択的に抑制する方法を探しに来ました。私はまだ方法を見つけていない。

だから私はこの警告をトラップし、自分でそれを抑制する。恥を私はこれを行う必要があります。 STRICTについては厳格です。

+2

コンストラクタに関しては、PHP 5.4以降では、_abstract_ベースクラスを拡張する際にメソッドシグネチャと互換性がなければなりません。そうしないと、致命的なエラーが発生します。 PHP 5.4以前ではE_STRICT警告もなく、基本クラスが抽象クラスでない場合、PHPのバージョンに関係なくメッセージはありません。 – MrWhite

+0

ありがとう、ありがとう、ありがとうこれは私が意図的にコンストラクタのエラーを抑制するPHPの言葉を見た最初の唯一の場所であり、非常に厄介なものです。これは、LSPの違反です。コンストラクタのサブタイプ引数を前提条件を強化することができるためです。メソッドの引数は、共変ではなく不変または反変でなければなりません。実際、PHPでは、オーバーライドされたメソッドの型付き引数の不変以外のものが可能な唯一のシナリオだと私は信じています。 –

関連する問題