Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】Understanding SOLID Principles

2025年09月19日に「Dev.to」が公開したITニュース「Understanding SOLID Principles」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

SOLID原則は、保守・拡張しやすいソフトウェアを作るための5つの設計ルールだ。SRPは「1つのクラスは1つの役割」、OCPは「既存コードを変更せず機能追加」、LSPは「子クラスは親と互換性」、ISPは「細かく具体的なインターフェースを使う」、DIPは「抽象的なものに依存する」というものだ。

出典: Understanding SOLID Principles | Dev.to公開日:

ITニュース解説

ソフトウェアを開発する際、どのようにプログラムの部品である「クラス」やコードを組み立てるかは非常に重要である。良い設計は、プログラムを修正しやすく、機能追加が容易で、そして何よりも理解しやすいものにする。SOLID原則とは、プログラマーがよりきれいで安全なコードを書くための、五つの重要な指針である。これらの原則を一つずつ簡単に解説する。

まず、一つ目は「単一責任の原則 (Single Responsibility Principle - SRP)」である。これは、一つのクラスはたった一つの仕事だけを担うべきであり、そのクラスが変更されるべき理由はただ一つであるべきだ、という考え方だ。例えば、ユーザーをデータベースに保存する機能と、ユーザーにウェルカムメールを送る機能の両方を一つのクラスが持っていたとする。この場合、データベースの保存方法が変わっても、メールの送信方法が変わっても、同じクラスを修正することになる。これは二つの異なる「責任」を持っているため、どちらかの変更が予期せぬ形でもう一方に影響を与えるリスクがある。この原則に従えば、データベースへの保存は「ユーザーリポジトリ」というクラスに、メール送信は「メールサービス」というクラスにそれぞれ分け、各クラスが独立した責任を持つようにする。これにより、一方の変更が他方に影響を与える可能性が低くなり、コードの管理が格段に楽になる。

二つ目は「開放/閉鎖の原則 (Open/Closed Principle - OCP)」である。これは、ソフトウェアの部品は新しい機能を追加する際には「開放」されているべきだが、既存のコードを変更する際には「閉鎖」されているべきだ、という原則である。つまり、新しい機能を追加する際に、既に動作している、テスト済みのコードを修正する必要がないようにするべきだ。例えば、支払い処理を行うプログラムで、クレジットカード払いとPayPal払いをif-else文で切り替えているとする。ここに新しい支払い方法(例えばApple Pay)を追加しようとすると、既存のif-else文に新しい条件を追加する必要がある。これは、既に動作している部分に手を入れることになるため、既存の機能にバグを混入させるリスクがある。この原則に従えば、支払い方法を「インターフェース」として定義し、各支払い方法をそのインターフェースを実装する別々のクラスとして作成する。こうすることで、新しい支払い方法を追加する際も、既存の支払い処理クラス自体は変更せず、新しい支払い方法のクラスを追加するだけで済むようになる。これにより、システムの安全性が保たれ、拡張性が高まる。

三つ目は「リスコフの置換原則 (Liskov Substitution Principle - LSP)」である。これは、親クラスが使える場所では、その子クラスも問題なく使えるべきだ、という原則である。もし親クラスの機能が子クラスで全く異なる動作をしたり、エラーになったりするようでは、プログラムの予測可能性が失われ、不安定になる。例えば、全ての鳥が「飛ぶ」ことができると仮定して、鳥のクラスに「飛ぶ」メソッドを定義したとする。しかし、ダチョウのように飛べない鳥がこの鳥クラスを継承した場合、「飛ぶ」メソッドを呼び出すとエラーになるように実装せざるを得ない。これは、ダチョウが鳥のクラスの「子クラス」であるにもかかわらず、親クラスと同じように扱えない状況であり、この原則に反する。この原則に従えば、飛べる鳥と飛べない鳥を明確に区別し、インターフェースやクラスの階層を適切に設計する。例えば、「飛ぶ鳥」というインターフェースを別に用意し、ダチョウのように飛べない鳥は基本的な「鳥」のクラスを継承するが、「飛ぶ鳥」インターフェースは実装しない。これにより、プログラムのどこに子クラスを置いても、期待通りの動作が保証され、安全性が高まる。

四つ目は「インターフェース分離の原則 (Interface Segregation Principle - ISP)」である。これは、クラスは必要としないメソッドを強制されるべきではない、という原則である。一つの大きなインターフェースに多くの機能を詰め込むのではなく、より小さく、特定の機能に特化したインターフェースに分割すべきだ。例えば、印刷、スキャン、ファックスの機能をすべて含む「機械」という大きなインターフェースがあったとする。このインターフェースを「基本的なプリンター」が実装しようとすると、スキャンとファックスの機能は持たないにもかかわらず、そのメソッドの実装を強制されてしまう。使わない機能の実装を強制されることで、そのクラスは不要な依存関係を持つことになり、理解しにくくなる。この原則に従えば、印刷機能だけを持つ「プリンター」インターフェース、スキャン機能だけを持つ「スキャナー」インターフェース、ファックス機能だけを持つ「ファックス」インターフェースのように、機能を細かく分割する。これにより、各クラスは自分が必要なインターフェースだけを実装すればよく、コードがよりすっきりと、そして柔軟になる。

五つ目は「依存性逆転の原則 (Dependency Inversion Principle - DIP)」である。これは、上位のモジュール(より高レベルのロジックを持つ部分)は下位のモジュール(具体的な実装を持つ部分)に依存すべきではなく、両方とも「抽象的なもの」に依存すべきだ、という原則である。また、抽象的なものは詳細に依存すべきではなく、詳細は抽象的なものに依存すべきだ、とも言える。例えば、ユーザー情報を保存する「ユーザーサービス」というクラスが、直接「MySQLデータベースへの保存」という具体的なクラスに依存しているとする。この場合、データベースをMySQLからMongoDBに変更したくなった時に、「ユーザーサービス」のコードも修正しなければならない。これは具体的な実装(MySQL)に強く依存しているため、変更に弱い。この原則に従えば、「ユーザーリポジトリ」というインターフェースのような「抽象的なもの」を定義する。そして、「ユーザーサービス」はこの抽象的なインターフェースに依存し、具体的なMySQLやMongoDBの実装クラスは、そのインターフェースを実装するようにする。これにより、「ユーザーサービス」は具体的なデータベースの種類を知る必要がなくなり、必要に応じてデータベースの実装を簡単に差し替えることができるようになる。これはシステムを柔軟にし、テストを容易にする効果がある。

これらのSOLID原則に従うことで、ソフトウェアは変更しやすくなり、テストが容易になり、そして規模が大きくなっても安定して動作し続けることができる。名前は専門的に聞こえるかもしれないが、その考え方は非常にシンプルである。それは、明確で、集中しており、将来にわたって拡張しやすいコードを書くこと、これに尽きる。

関連コンテンツ