堅実な原則

Linda Hamilton
リリース: 2024-10-21 06:12:02
オリジナル
1000 人が閲覧しました

SOLID Principles

SOLID は、オブジェクト指向プログラミング (OOP) におけるコードの管理性とスケーラビリティを強化するために設計された基本原則の集合です。これは 5 つの重要な原則で構成されています:

  1. S 責任原則 — SRP
  2. O ペン閉鎖原則 — OCP
  3. L iskov の置換原理 — LSP
  4. インターフェース分離原則 — ISP
  5. D 依存性反転原理 — DIP

これらの原則は、2000 年代初頭に Robert C. Martin (ボブおじさんとしても知られる) によって導入され、それ以来ソフトウェア開発コミュニティで広く採用されています。 SOLID 原則に従うことで、開発者は理解しやすく、変更し、拡張しやすいコードを作成でき、より堅牢で保守しやすいソフトウェア システムを実現できます。

単一責任原則 (SRP)

単一責任原則は、OOP と SOLID における最初で最も基本的な原則です。名前のとおり、この原則は「1 つのクラスは、処理すべき特定の責任を 1 つだけ持つべきである」ことを意味します。

Invoice というクラスがあり、このクラスには、generateInvoice() と saveToFiles() という 2 つのメソッドが含まれているとします。

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }

  public void saveToFiles() {
    // code to save invoice as a file.
  }
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

Invoice クラスには 2 つの責任があるため、これは良い習慣ではありません。より良いアプローチは、これらの機能を専用のクラスに分割することです。

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }
}

public class FileManager {
  public void saveToFiles(Invoice invoice) {
    // code to save invoice as a file.
  }
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

ここでは、ユースケースに 2 つのクラスがあることがわかります。

  • 請求書の生成
  • ファイルに保存

SRPに従うメリット

  • コード構成の改善 : 懸念事項をさまざまなクラスに分割することで、コードベースがより整理され、ナビゲートしやすくなります。
  • 保守性の向上 : クラスの責任が 1 つである場合、その目的を理解し、予期しない副作用を発生させることなく変更を加えることが容易になります。
  • 再利用性の向上 : 単一の責任を持つクラスは、アプリケーションの別の部分や他のプロジェクトでも再利用できる可能性が高くなります。
  • より簡単なテスト : 単一の責任を持つクラスは通常、小規模で集中力が高いため、個別にテストすることが容易になります。

オープンクローズ原則 (OCP)

オープン-クローズの原則は、SOLID のもう 1 つの中心的な原則です。この原則は、1997 年に Bertrand Meyer によって導入されました。この原則の背後にある考え方は、「ソフトウェア成果物 (クラス、モジュール、関数) は拡張に対してオープンであるべきですが、変更に対してはクローズされるべきである。」

です。

例:

たとえば、 Shape というクラスがあるとします。このクラスを使用して、形状の面積を計算できます。

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }

  public void saveToFiles() {
    // code to save invoice as a file.
  }
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

上記のコードでは、新しいシェイプを追加するには既存の Shape クラスを変更する必要がありますが、これは良い習慣とは見なされません。

以下は、このシナリオにオープンクローズ原則を適用する方法を示すコード例です。

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }
}

public class FileManager {
  public void saveToFiles(Invoice invoice) {
    // code to save invoice as a file.
  }
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

OCP を適用すると、現在の実装を変更せずに、必要に応じて多くの形状を追加できます。

注: インターフェイスの使用が OCP を実現する唯一の方法ではありません。

OCP に従う利点

  • バグのリスクの軽減 : 既存のコードを変更しないことで、新しいバグが発生したり、既存の機能が壊れたりするリスクが最小限に抑えられます。
  • 保守性の向上 : OCP に従うコードは、既存のコードベースを変更せずに新しい機能を追加できるため、保守と拡張が容易になります。
  • 柔軟性の強化 : 抽象化とポリモーフィズムの使用により、より柔軟で適応性のある設計が可能になり、変化する要件への対応が容易になります。

リスコフの置換原理 (LSP)

リスコフの置換原理は、OOP におけるもう 1 つの重要な原理です。これは、1987 年にデータ抽象化に関するカンファレンスの講演中に Barbara Liskov によって導入されました。

原則は、「スーパークラスのオブジェクトは、プログラムの正確さを変えることなく、そのサブクラスのオブジェクトと置き換え可能であるべきである」と述べています。

たとえば、Circle と Rectangle が Shape のサブタイプである場合、Shape オブジェクトを Circle または Rectangle オブジェクトに問題なく置き換えることができるはずです。

public class Shape {
    private String shapeType;
    private double radius;
    private double length;
    private double width;

    public Shape(String shapeType, double radius, double length, double width) {
        this.shapeType = shapeType;
        this.radius = radius;
        this.length = length;
        this.width = width;
    }

    public double area() {
        if (shapeType.equals("circle")) {
            return Math.PI * (radius * radius);
        } else if (shapeType.equals("rectangle")) {
            return length * width;
        } else {
            throw new IllegalArgumentException("Unknown shape type");
        }
    }
}

// Usage
public class Main {
    public static void main(String[] args) {
        Shape circle = new Shape("circle", 5, 0, 0);
        Shape rectangle = new Shape("rectangle", 0, 4, 6);

        System.out.println(circle.area());
        System.out.println(rectangle.area());
    }
}
ログイン後にコピー
ログイン後にコピー

この例で示したように、リスコフ置換原則に従うということは、スーパークラス インスタンスをサブクラス インスタンスにシームレスに置換できるはずであることを意味します。

LSP に従う利点

  • コードの再利用性の向上 : サブタイプを基本タイプの代わりに使用できるようにすることで、基本タイプを使用するコードはそのサブタイプのいずれでも動作し、コードの再利用が促進されます。
  • 保守性の強化 : LSP に従うコードは、コードベースを変更または拡張するときにバグが発生するリスクが軽減されるため、保守が容易になります。
  • テスト容易性の向上 : LSP を使用すると、クラスとそのサブタイプの単体テストを簡単に作成できます。テストは基本型に対して作成でき、すべてのサブタイプに対して機能するはずです。

インターフェース分離原則 (ISP)

インターフェース分離原則は、Robert C. Martin によって導入された 5 つの SOLID 原則の 1 つです。 「クライアントは、使用しないインターフェースに強制的に依存すべきではない」と述べられています。

言い換えると、1 つの汎用インターフェイスを使用するよりも、タスク固有のインターフェイスを多数使用する方が優れています。

以下の例は、汎用インターフェースの使用法を示しています。

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }

  public void saveToFiles() {
    // code to save invoice as a file.
  }
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

MultifunctionPrinter のような汎用インターフェイスを使用すると、不要なメソッドの実装が強制され、これは悪い習慣と考えられます。このシナリオにインターフェイス分離原則を適用する方法を見てみましょう。

インターフェース

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }
}

public class FileManager {
  public void saveToFiles(Invoice invoice) {
    // code to save invoice as a file.
  }
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

実装

public class Shape {
    private String shapeType;
    private double radius;
    private double length;
    private double width;

    public Shape(String shapeType, double radius, double length, double width) {
        this.shapeType = shapeType;
        this.radius = radius;
        this.length = length;
        this.width = width;
    }

    public double area() {
        if (shapeType.equals("circle")) {
            return Math.PI * (radius * radius);
        } else if (shapeType.equals("rectangle")) {
            return length * width;
        } else {
            throw new IllegalArgumentException("Unknown shape type");
        }
    }
}

// Usage
public class Main {
    public static void main(String[] args) {
        Shape circle = new Shape("circle", 5, 0, 0);
        Shape rectangle = new Shape("rectangle", 0, 4, 6);

        System.out.println(circle.area());
        System.out.println(rectangle.area());
    }
}
ログイン後にコピー
ログイン後にコピー

ISP を適用することで、プリンター、スキャナー、ファックスなどの、より小さな役割固有のインターフェース — に分割します。これにより、各クラス (BasicPrinter、AdvancedPrinter、FaxMachine など) が関連する機能のみを実装できるようになり、モジュール性が促進され、不要な依存関係が削減されます。

ISP をフォローするメリット

  • モジュール化された再利用可能なコード : 大規模なインターフェイスをより小さく、より具体的なインターフェイスに分割することで、コードはよりモジュール化され、再利用可能になります。クラスまたはモジュールは必要なインターフェイスのみを実装できるため、不必要な依存関係が減り、システムのさまざまな部分でコードを再利用しやすくなります。
  • コードの複雑さの軽減 : クラスまたはモジュールが必要なインターフェイスのみに依存すると、コードの複雑さが軽減され、理解しやすくなります。これは、開発者が不要なメソッドや依存関係に対処する必要がないためです。これらは、特定の使用例には関係ありません。
  • 保守性の向上 : より小さく、より焦点を絞ったインターフェースにより、コードの保守が容易になります。 1 つのインターフェースへの変更がシステムの他の部分に影響を与える可能性が低くなり、バグが発生したり、既存の機能が破壊されたりするリスクが軽減されます。
  • テスト容易性の向上 : より小さく、より焦点を絞ったインターフェイスにより、個々のコンポーネントの単体テストを簡単に作成できます。これは、無関係なメソッドや依存関係の影響を受けることなく、テストが特定の動作に焦点を当てることができるためです。
  • 柔軟性の向上 : ISP に準拠することで、システムの柔軟性が高まり、拡張や変更が容易になります。新しい機能や要件は、システム全体に影響を与えることなく、新しいインターフェイスを作成するか既存のインターフェイスを変更することで追加できます。

依存関係逆転の原則 (DIP)

依存関係逆転の原則は、SOLID の最後の原則です。これはロバート C. マーティンによっても紹介されました。これにより、コードの疎結合が促進されます。

DIP はいくつかの点を述べています:

  • 高レベルのモジュールは低レベルのモジュールに依存しないでください。
  • どちらも抽象化に依存する必要があります。
  • 抽象化は詳細に依存すべきではありません。
  • 詳細は抽象化に依存する必要があります。

簡単に言うと、クラスは他の特定のクラス (具象実装) に直接依存するのではなく、インターフェイスまたは抽象クラス に依存する必要があります。これにより、依存クラスを変更せずに実装を交換できるため、コードがより柔軟になり、保守が容易になります。

密結合コード(DIPなし)

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }

  public void saveToFiles() {
    // code to save invoice as a file.
  }
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

上の例に示すように、Computer クラスは Keyboard クラスに直接依存します。

疎結合コード(DIPあり)

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }
}

public class FileManager {
  public void saveToFiles(Invoice invoice) {
    // code to save invoice as a file.
  }
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

現在、コンピューターは特定のキーボードではなく、InputDevice インターフェイスに依存しています。これにより、Computer クラスを変更せずに、WirelessKeyboard などの別の入力デバイスに簡単に切り替えることができます。

DIP に従うメリット

  • 疎結合 : 具体的な実装ではなく抽象化に依存することで、コードの結合が弱くなり、他の部分に影響を与えることなくシステムの一部を変更することが容易になります。
  • 保守性の向上 : 下位モジュールの変更は上位モジュールに影響を与えないため、システムの保守と拡張が容易になります。
  • テスト容易性の強化 : 低レベル モジュールのモック実装を使用して高レベル モジュールをテストできるため、テストがより速く、より信頼性が高くなります。
  • 再利用性の向上 : 高レベルのモジュールは、依存する低レベルのモジュールを変更することなく、さまざまなコンテキストで再利用できます。

結論

結論として、SOLID 原則: 単一責任、オープン/クローズ、リスコフ置換、インターフェイス分離、および依存性反転は、オブジェクト指向プログラミングでクリーンで保守可能でスケーラブルなコードを記述するための重要なガイドラインを提供します。

これらの原則に従うことで、開発者は理解しやすく、変更し、拡張しやすいシステムを作成でき、最終的にはより高品質のソフトウェアとより効率的な開発プロセスにつながります。

まとめ

この記事を読んでいただきありがとうございます! SOLID の原則と、それを適用してプロジェクトを強化する方法をしっかりと理解していただけたと思います。

フォローしてください:
  • LinkedIn — @nsadisha
  • GitHub — @nsadisha
  • 中 — @nsadisha
  • Dev.to — @nsadisha

— サディシャ・ニムサラ

以上が堅実な原則の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート