SOLID は、オブジェクト指向プログラミング (OOP) におけるコードの管理性とスケーラビリティを強化するために設計された基本原則の集合です。これは 5 つの重要な原則で構成されています:
これらの原則は、2000 年代初頭に Robert C. Martin (ボブおじさんとしても知られる) によって導入され、それ以来ソフトウェア開発コミュニティで広く採用されています。 SOLID 原則に従うことで、開発者は理解しやすく、変更し、拡張しやすいコードを作成でき、より堅牢で保守しやすいソフトウェア システムを実現できます。
単一責任原則は、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 つのクラスがあることがわかります。
オープン-クローズの原則は、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 を実現する唯一の方法ではありません。
リスコフの置換原理は、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()); } }
この例で示したように、リスコフ置換原則に従うということは、スーパークラス インスタンスをサブクラス インスタンスにシームレスに置換できるはずであることを意味します。
インターフェース分離原則は、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 など) が関連する機能のみを実装できるようになり、モジュール性が促進され、不要な依存関係が削減されます。
依存関係逆転の原則は、SOLID の最後の原則です。これはロバート C. マーティンによっても紹介されました。これにより、コードの疎結合が促進されます。
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 クラスに直接依存します。
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 などの別の入力デバイスに簡単に切り替えることができます。
結論として、SOLID 原則: 単一責任、オープン/クローズ、リスコフ置換、インターフェイス分離、および依存性反転は、オブジェクト指向プログラミングでクリーンで保守可能でスケーラブルなコードを記述するための重要なガイドラインを提供します。
これらの原則に従うことで、開発者は理解しやすく、変更し、拡張しやすいシステムを作成でき、最終的にはより高品質のソフトウェアとより効率的な開発プロセスにつながります。
この記事を読んでいただきありがとうございます! SOLID の原則と、それを適用してプロジェクトを強化する方法をしっかりと理解していただけたと思います。
フォローしてください:
— サディシャ・ニムサラ
以上が堅実な原則の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。