2

次の名前のクラスがあるとしSprinterます。

public class Sprinter {

    protected int travelMeters;

    public void run(int seconds) {
        this.travelMeters = 9 * seconds;
    }

    public int getTravelMeters(){
        return travelMeters;
    }
}

そしてSprintGenius継承する型Sprinter

class SprintGenius extends Sprinter {

    public void run(int seconds) {
        this.travelMeters = 10 * seconds;
    }
}

論理的には、タイプごとに 1 つずつ、2 つの単体テスト クラスを作成する必要があります。

Sprinter単体テスト内では、次のようになります。

@Before
public void setUp() {
  Sprinter sprinter = new Sprinter();
}

public void testSprinterShouldRun90metersWithin10Seconds() {
  sprinter.run(10);
  assertEquals(sprinter.getTraveledMeters(),90);
}

SprintGenius単体テスト内では、次のようになります。

@Before
public void setUp() {
  Sprinter sprinter = new SprintGenius();
}

public void testSprinterShouldRun100metersWithin10Seconds() {
  sprinter.run(10);
  assertEquals(sprinter.getTraveledMeters(),100);
}

上記の両方のテストで、10 秒以内に移動したメートルの量をテストします。

明らかに、これらの両方のテストは緑色になります。

しかし、リスコフの置換原理の違反についてはどうでしょうか?

実際、どのクライアント コードも、スプリンターが 9 秒以内に正確に 10 メートル走ることを期待する必要があります。

3 つの解決策 (最初の 2 つの解決策は、すべてのチームの開発者にルールが通知され、全員が Liskov の概念を十分に習得していなくても、認​​めて保持する必要があります)

1)Sprinterクラスでは、各テストを複製しますが、今回Sprinter sprinter = new SuperGenius()は 90 メートルを想定しています。=> 何が失敗するべきか、これこそまさに私たちが望んでいたことです! => Liskov の原則に違反しないようにします。

2)SprintGeniusクラスでは、まったく同じ期待に基づいて、基本クラスに基づいて各テストの同様の「クローン」を常に追加します。したがって、2 つの異なるテストがある場合、最終的に 4 つのテストになります。2 は Sprinter を として宣言しSprinter、2 は として宣言SprinterSprintGeniusます。

3) 具象クラスから継承することはありません (この投稿を読んで最初に反応したのはこれだと思います:))。この問題が起こらないように。

多くの開発者が Liskov 原則を無視し、コンポジションや異なる継承階層などの別のより良い方法を使用する代わりに、具象クラスから継承するように誘惑されることが多いという事実に基づいて、Liskov 置換原則違反を防ぐためのベストプラクティスは何ですか?

開発者が私が書いたクラスを (私に言わずに..) 継承し、それを異種混合Sprinterリストの共有された巨大なリスト内に挿入し、「こんにちは、奇妙な振る舞いだ!」そして何時間ものデバッグ時間...

もちろん、すべての具象クラスを「final」と宣言したくありません:)

4

2 に答える 2

4

単体テストは特定のモジュールのテストに関するものであり、それよりも幅広いものには使用できませんし、使用すべきではありません。Liskov Substitution Principle への準拠は、モジュールの範囲ではなく、システムの範囲におけるより広範な問題です。また、コードでテストするものではありません。これは純粋な設計上の問題であり、実装には関係ありません。自動ツールで LSP を実施できるとは思えません。これは、設計レビュー中に処理し、後でコード レビュー中に処理する必要があります (設計への準拠を確認する必要があります)。

于 2012-11-02T11:24:19.263 に答える
3

これは、リスコフ置換原則の遵守に違反していません。これは悪い設計です。両方のクラスの動作は異なりますが、データは異なります。したがって、クラスは 1 つだけにする必要があり、クライアントは、任意のスプリンターがその速度に比例して特定の距離を走ることを期待する必要があります。

したがって、速度プロパティを追加し、具体的な動作を持つ単一のクラスを持つ必要があります。

その後、新しい動作を備えた本当に拡張されたクラスを作成することについて考え、テストについて考えることができます。

この速度パラメーターを使用すると、別の種類のランナーが別の速度で走ったとしても、Liskov Substitution の原則が破られることはありません。

あなたの質問は次のようなものです: 人物の名前を「ピーター」から「ロバート」に変更したため、人物を拡張する私のクラスはテストに合格しません。

これはそのような質問の悪い例です。適切な例については、はい、それをテストするのは良い習慣だと思いますが、それは非常に防御的なアプローチです。おそらく、与えられた時間をより良いテストの作成に使用できます。また、そのテストは非常に短期間で古くなります。古い動作が適切に機能することを確認するためだけに、新しいサブクラスにテストを追加することは困難です。

于 2012-11-02T11:30:47.300 に答える