web-dev-qa-db-ja.com

Liskov置換の原則:サブタイプには、型には存在しない追加の動作がいくつか実装されていますが、これはこのLSPの違反ですか?

私はよりよく、よりクリーンなコードを書くために、SOLIDの原則について学習しています。この中で、LSPは適切に把握するのが少し難しいことがわかっています。

私の疑問は、サブタイプSに余分なメソッドがあり、タイプTになかった場合、これは常にLSPの違反になるのでしょうか?はいの場合、どのようにしてクラスをextendできますか?

たとえば、Birdタイプがあるとします。そしてそのサブタイプはEagleHumming Bird。現在、両方のサブタイプには、Birdとしていくつかの共通の動作があります。 しかしEagleもまた、私が使用したい(一般的なBirdタイプにはない)略奪的な動作をします。したがって、今これを行うことはできません:

Bird bird = new Eagle();

では、Eagleにこれらの追加の動作を与えると、LSPが壊れますか?

はいの場合、それはLSP違反を引き起こすため、クラスを拡張できないことを意味しますか?

class Eagle extends Bird {
   //we are extending Bird since Eagle has some extra behavior also
}

クラスの拡張は、Open/Closedの原則に従って許可する必要がありますか?

よろしくお願いします!はっきりとわかるように、LSPは私を他のものと同じように混乱させています。

編集: これを参照SO回答。 この場合も、CarChangeGearのような追加の動作がある場合、LSPに違反します。では、LSPに違反せずにクラスを拡張するにはどうすればよいでしょうか。

8
user270386

私の疑問は、サブタイプSに余分なメソッドがあり、タイプTになかった場合、これは常にLSPの違反になるのでしょうか?

非常に簡単な答え:いいえ

LSPのポイントは、STの代わりに使用できることです。したがって、Tdelete関数を実装する場合、Sもそれを実装し、呼び出されたときに削除を実行する必要があります。ただし、Sは、Tが提供する機能に加えて、無料で機能を追加できます。 Tのコンシューマーは、Sが指定されている場合、この追加の機能を認識しませんが、Sのコンシューマーが直接利用できるようにすることができます。

原則がどのように違反される可能性があるかについての非常に不自然な例は次のとおりです。

class T
{
    bool delete(Item x)
    {
        if (item exists)
        {
            delete it
            return true;
        }
        return false;
    }
}

class S extends T
{
    bool delete(Item x)
    {
        if (item doesn't exist)
        {
            add it
            return false;
        }
        return true;
    }
}

少し複雑な答え:いいえ、基本タイプの状態やその他の予想される動作に影響を与えない限り

たとえば、以下は違反です。

class Point2D
{
    private readonly double _x;
    private readonly double _y;

    public virtual double X => _x;
    public virtual double Y => _y;

    public Point2D(double x, double y) => (_x, _y) = (x, y);
}

class MyPoint2D : Point2D
{
    private double _x;
    private double _y;

    public override double X => _x;
    public override double Y => _y;

    public MyPoint2D(double x, double y) : 
        base(x, y) => (_x, _y) = (x, y);

    public void Update(double x, double y) => (_x, _y) = (x, y);
}

タイプPoint2Dは不変です。その状態は変更できません。 MyPoint2Dを使用して、この動作を意図的に回避して変更可能にしました。これはPoint2Dの履歴制約を破るので、LSPの違反になります。

10
David Arno

もちろん違います。 Eagleオブジェクトが、Birdまたはサブクラスを必要とする任意のコードで使用でき、Birdが動作するように動作する場合、問題ありません。

もちろんEagleの動作は、そのようなオブジェクトであることを認識しているコードでのみ使用できます。 Birdオブジェクトを必要とする任意のコードを使用できる一方で、一部のコードはEagleオブジェクトを明示的に作成してEagleオブジェクトとして使用することが期待されます。

3
gnasher729