ホームページ  >  記事  >  バックエンド開発  >  親クラスのメソッドとリスコフ置換原則を無視する PHP7.2 に関連する問題の分析

親クラスのメソッドとリスコフ置換原則を無視する PHP7.2 に関連する問題の分析

藏色散人
藏色散人転載
2021-11-16 15:14:521439ブラウズ

パラメータ型関数と Liskov 置換原則を省略した PHP 7.2 のサブクラス カバレッジ メソッドについて詳しく説明します

PHP 7.2 は、新しい改善点に関心がある限り、しばらくリリースされています。新しいバージョン PHP の開発を見たことがあるはずです。ここでは、誤解される可能性のある新機能を 1 つだけ詳しく説明します。

PHP 7.2 は、サブクラスが親クラス メソッドをオーバーライドするときに、親クラス メソッドによって定義されたパラメータの型 (型ヒント) を無視できます:

class Foo
{
    public function bar(SomeClass $obj) {}
}
class Foobar extends Foo
{
    public function bar($obj) {} // 这在 PHP7.2 版本之前是会报错的
}

この関数について話すとき、いくつかの Web サイトの紹介を見ました。その目的はリファクタリングを容易にすることだと言われています。親クラスのメソッドのパラメータの型が将来変更された場合、サブクラスはそれらをすべて変更する必要はありません。それは合理的だと思われます。このステートメントによると、暗黙の意味は次のとおりです。サブクラスが親クラス メソッドのパラメーターの型を無視した場合でも、サブクラスが呼び出されたときにパラメーターの型がチェックされます。

<?php
class Foo
{
}
class Bar
{
    public function setFoo(Foo $foo)
    {
    }
}
class BarKid extends Bar
{
    public function setFoo($foo)
    {
    }
}
$kid = new BarKid;
$kid->setFoo(&#39;I am a string!&#39;);

上記のステートメントが正しい場合、setFoo は文字列パラメーターを受け入れるときにエラーを報告するはずですが、上記のコードは 7.2 ではエラー メッセージを報告しません。ただし、サブクラスの setFoo メソッドでパラメータの型を追加すると、すぐにエラーが報告されます。私の小さな Web サイトを除いて、インターネット上の多くの意見は信頼できないことを忘れないでください...

上記の実験は、サブクラス メソッドがパラメーターの型を省略できることを示しており、その目的は決して再構成を容易にすることではありません。では、本当の目的は何でしょうか?

PHP 7.1 には、「メソッドまたは関数のパラメータと戻り値の型を null に設定できるかどうか」という新機能があります。一見厄介なルールがあります。「サブクラス メソッドのパラメータ型範囲は緩和されます (つまり、親クラス パラメータが null にできない場合、サブクラス パラメータは null をサポートできます)。しかし、戻り値の型は厳しくなります (親クラスが null をサポートできない場合)。 null を返す、サブクラスのパラメータは null をサポートできる)クラスは機能してはなりません。親クラスが null を返すことができる場合、サブクラスは null を返す必要はありません)」私はその時、それが「リスコフ置換原則」のためであると簡単に言いました。 」とありましたが、詳しくは紹介していませんでした。私の周囲では OOP の原則に注目している PHPer は多くありませんが、エンジニアなら知っておくべき内容だと思うので紹介したいと思います。

Liskov の置換原理は単純です。親クラスがどこに存在しても、サブクラスに置き換えられれば実行できます。つまり、サブクラスは何も考えずに親クラスを置き換えることができます。 実は、この原理は言語設計の観点から見ると、自然法則の模倣であると考えています2018-09-29 補足:単なる「模倣」ではありません。新しいブログ「ペンギンは鳥ではありません」を読むことができます。

たとえば、人間はお酒、お茶、コーラなどあらゆる飲み物を飲むことができますが、哺乳類である人間はどうしても水を飲むことができますよね。しかし、逆に言えば、哺乳類は水は飲めますが、お酒やお茶、コーラは飲めない可能性があるため、人間は哺乳類の下位カテゴリーに属します。

言語設計の観点から見ると、サブクラスは親クラスの拡張バージョンである必要があります。つまり、親クラスよりも多くのオブジェクト型を処理でき、オーバーライドされたメソッド パラメータを拡張できる必要があります。タイプもその理由です。

少し複雑になるかもしれない戻り値の型について話しましょう。サブクラスが戻り値の範囲を狭める必要があるのはなぜでしょうか?実際、あるメソッドの戻り値が別のメソッドのパラメータとして使用されることを想定している限り、考えるのは簡単です。たとえば、「Fruit Beverage Factory」クラスには、「Fruit Juice」を返し、それを「Kid」の「Drink」メソッドに渡す「Production」メソッドがあります。 "Fruit Beverage Factory" のサブクラスである "Orange Juice Factory" クラスがあります。その "Production" メソッドの戻り値の型は絞り込まれており、"Orange Juice" のみを返すことができます。これは引き続き "children" に与えられます。ドリンク」と表示されませんが問題ありません。

別の反例を挙げてください。 「Fruit Beverage Factory」の別のサブクラスが出現し、その「Production」メソッドがフルーツ ジュースに加えてフルーツ ワインを返すことができる場合、このサブクラスは明らかに、子供が飲酒する危険がある親クラスを置き換えることはできません。

リスコフの置換原則について話した後、7.2 でのこの改善を見てみましょう。この時点で、これが実際にはリスコフの原則の現れであることを知っておく必要があります。現在、PHP における置換原則の実装は不完全です。このバージョンでは「親クラスには戻り値の型がなく、サブクラスには戻り値の型を持つことができる」こともサポートされていると思う人もいるかもしれません。残念ながら、少なくともバージョン 7.2 ではサポートされていないため、自分で試すことができます。

7.2 のもう 1 つの新機能は、object を任意のオブジェクトのタイプとして使用できることです。公式の例を参照してください:

<?php
function test(object $obj) : object
{
    return new SplQueue();
}
test(new StdClass());

実際、7.2 のリリース前には、同じく置換原則に基づいて、「サブクラスがオブジェクト型を使用して、オーバーライドされたメソッド オブジェクト パラメータの型を置き換えることができるかどうか」という質問がありました。 、しかし結局、投票は通過しませんでした。理由はわかりませんが、少なくとも誰かがそれについて言及しました。

さらに、現在、PHP は Java のようにオーバーロードできず、オーバーライドされたメソッドの型を指定する方法がありません (現在、型は直接削除することしかできませんが、これは少し大雑把すぎます):

<?php
class Foo
{
}
class FooFoo extends Foo
{
}
class Bar
{
    public function foo(FooFoo $foo)
    {
    }
}
class BarBar extends Bar
{
    public function foo(Foo $foo) // 依然会报『子类不兼容父类方法格式』的错误
    {
    }
}

でも、PHP は常に変化していますよね? PHP バージョンは最近非常に速く反復されており、PHP はより多くの OOP 機能をサポートする言語になると確信しています。

推奨学習: 「PHP7 チュートリアル

以上が親クラスのメソッドとリスコフ置換原則を無視する PHP7.2 に関連する問題の分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はchrisyue.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。