ホームページ >Java >&#&チュートリアル >Typescript と Java で「Liskov 置換原則」を適用する

Typescript と Java で「Liskov 置換原則」を適用する

PHPz
PHPzオリジナル
2024-08-30 06:01:32850ブラウズ

Aplicando o

コンセプト

インターフェース

インターフェイスは、クラスが実装する必要があるコントラクト、またはメソッドとプロパティのセットを定義します。インターフェイスは、クラスが特定の形式に従っていることを確認するために使用されますが、メソッドの実装は提供せず、メソッドのシグネチャのみを提供します。

クラスがインターフェイスを実装するときは常に、そのインターフェイスのすべてのコントラクト (メソッドと属性) に署名します。各属性とメソッドは強制的に実装されます。

固体

SOLID は、ロバート C. マーティン (アンクル ボブ) によって提案された、オブジェクト指向プログラミングの 5 つの基本原則を表す頭字語です。ここで彼の記事の詳細を読むことができます。
これらの原則は、コードの構造とメンテナンスを改善し、コードをより柔軟でスケーラブルにし、理解しやすくすることを目的としています。このような原則は、プログラマーがより組織化されたコードを作成し、責任を分割し、依存関係を減らし、リファクタリング プロセスを簡素化し、コードの再利用を促進するのに役立ちます。

LSPについて

頭字語の「L」は、「リスコフ置換原理」 を表します。ボブおじさんがこの原則を定義するために使用したフレーズは次のとおりです:

「派生クラスは基本クラスを完全に置き換えることができなければなりません」

したがって、コードを変更せずに派生クラスが基本クラスを置き換えることができるように、派生クラスを基本クラスにできる限り近づけることをお勧めします。

この原則は、データの抽象化と型の理論に基づいて、1988 年に Barbara Liskov によって導入されました。 1986 年に Bertrand Meyer によって広められた Design by Contracts (DBC) の概念に由来します。

この原則の別の仕様は次のとおりです:

サブタイプは、何の心配もなく基本タイプとして使用する必要があります。

プログラミングでは、変更や予期せぬことが問題を引き起こす可能性があります。システム機能を置き換える必要がある場合、新しい機能が同じ種類の情報を提供する必要があります。そうしないと、システムが故障する可能性があります。クラス S が基本クラス T と同じ動作をするようにするには、クラス S 間の類似性の整合性を保証するために、新しい機能を実装するための必須メソッドを定義するコントラクト (インターフェイスまたは抽象クラス) を使用することが不可欠です。そしてクラスT。

実用化

Sparrow と Ostrich の 2 つの子クラスで使用される fly() メソッドを持つ Bird 基本クラスを考えてみましょう。

ファイル:bird.java

class Bird {
    void fly() {
        System.out.println("I can fly!");
    }
}

class Sparrow extends Bird {
    // Herda o comportamento de 'fly' da classe 'Bird'
}

class Ostrich extends Bird {
    @Override
    void fly() {
        throw new UnsupportedOperationException("I cannot fly");
    }
}

ファイル:bird.ts

class Bird {
  fly() {
    console.log("I can fly!");
  }
}

class Sparrow extends Bird {}

class Ostrich extends Bird {
  fly() {
    throw new Error("I cannot fly");
  }
}

発生した問題

ここで、Sparrow クラスは LSP に準拠しています。これは、スズメは実際に飛ぶことができるためです。ただし、Ostrich クラスは、動作を根本的に変更する方法で voo() メソッドをオーバーライドし、Ave クラスによって設定された期待を破るため、LSP に違反します。

修正方法は?

Sparrow クラスと Ostrich クラスのそれぞれの特性をコントラクト (インターフェイスまたは抽象クラス。ここではインターフェイスを使用します) に分割して LSP を適用する必要があります。これらのコントラクトに署名して、それぞれの動作を調整する必要があります。

ファイル:bird.java

interface Bird {
    String getName();
    void makeSound();
}

interface FlyingBird extends Bird {
    void fly();
}

class Sparrow implements FlyingBird {
    private String name;

    public Sparrow(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public void makeSound() {
        System.out.println("Chirp chirp!");
    }

    @Override
    public void fly() {
        System.out.println(this.name + " is flying!");
    }
}

class Ostrich implements Bird {
    private String name;

    public Ostrich(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public void makeSound() {
        System.out.println("Boom boom!");
    }
}

public class Main {
    public static void main(String[] args) {
        Sparrow sparrow = new Sparrow("Little Sparrow");
        sparrow.makeSound(); // Chirp chirp!
        sparrow.fly(); // Little Sparrow is flying!

        Ostrich ostrich = new Ostrich("Ostrich");
        ostrich.makeSound(); // Boom boom!
        ostrich.fly(); // Error: Method 'fly' does not exist on 'Ostrich'
    }
}

ファイル:bird.ts

interface Bird {
  name: string;
  makeSound(): void;
}

interface FlyingBird extends Bird {
  fly(): void;
}

class Sparrow implements FlyingBird {
  name: string;

  constructor(name: string) {
    this.name = name;
  }

  makeSound() {
    console.log("Chirp chirp!");
  }

  fly() {
    console.log(`${this.name} is flying!`);
  }
}

class Ostrich implements Bird {
  name: string;

  constructor(name: string) {
    this.name = name;
  }

  makeSound() {
    console.log("Boom boom!");
  }
}

const sparrow = new Sparrow("Little Sparrow");
sparrow.makeSound(); // Chirp chirp!
sparrow.fly(); // Little Sparrow is flying!

const ostrich = new Ostrich("Ostrich");
ostrich.makeSound(); // Boom boom!
ostrich.fly(); // Error: Method 'fly' does not exist on 'Ostrich'

分析

訂正説明
Bird Interface: makeSound() など、すべての Bird に共通の動作を定義します。すべての鳥はこのインターフェースを実装する必要があります。

FlyingBird インターフェイス: Ave から継承し、飛ぶことができる鳥に固有の fly() 動作を追加します。

Sparrow クラス: スズメは飛ぶことができるため、FlyingBird インターフェイスを実装します。このクラスは、音の発生と飛行の両方の動作を定義します。

Ostrich クラス: ダチョウは飛べないため、Bird インターフェースのみを実装します。このクラスには fly() メソッドがないため、LSP に違反しません。

結論

LSP は、コードがモジュール化され、再利用可能で、保守が容易であることを保証するために重要です。 LSP に違反すると、新しいサブクラスが導入されたとき、または既存のサブクラスが変更されたときに壊れる脆弱なコードが発生する可能性があります。これは、スーパークラスに依存するコードの部分で予期しない動作を引き起こす可能性があるためです。

サブタイプ置換を使用すると、モジュールを変更せずに拡張できます。これは、オープン/クローズド原則 (OCP) によって提供される柔軟性に不可欠であり、リスコフ置換原則によって可能になります。コントラクト (インターフェイスまたは抽象クラスを介して実装される) は安全な設計に不可欠ですが、レガシー ソフトウェアでよくあるエラーを回避するには、プログラマがコントラクトをよく理解する必要があります。また、問題のコントラクトを観察するだけで、コードの実装方法と使用方法に関する貴重なガイダンスも提供されます。

実際的な意味

  1. サブクラスを設計するときは、スーパークラスが使用される場所であればどこでも、バグが発生したり特別な処理を必要としたりすることなくサブクラスを使用できることを確認してください。
  2. スーパークラスの予期される動作に違反するサブクラスの作成は避けてください。メンテナンスの問題や予期しないバグが発生する可能性があります。

リスコフ置換原則を理解して適用することは、開発者がより予測可能で安定したオブジェクト指向システムを作成するのに役立ちます。

以上がTypescript と Java で「Liskov 置換原則」を適用するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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