ソリッド原則について 〜PHPにおける実践例〜
■ はじめに
こんにちは。エンジニアの西崎です。
私は普段、PHP・Laravel を使用して開発をしているんですが、
最近 SOLID原則について調べる機会があったので、具体的な例を紹介しつつ記事にまとめていきたいと思います。
SOLID原則は、オブジェクト指向プログラミングの重要な開発原則で、ソフトウェア開発において、コードの品質を向上させるための重要なガイドラインです。これらの原則を実践することで、コードはより柔軟で保守しやすくなります。
■ 1. 単一責任原則(SRP)
まずは、単一責任原則についてです。これは、1 つのクラスが 1 つの責務だけを持つべきという原則です。よくあるのは、1 つのクラスが複数の責務を担当してしまうことです。
例えば、ユーザーの認証とデータベースへのアクセスを同じクラスで行っている場合があります。
Before
class AuthAndDatabase {
public function authenticate($username, $password) {
// 認証ロジック
}
public function saveToDatabase($data) {
// データベースへの保存ロジック
}
}
After
class Auth {
public function authenticate($username, $password) {
// 認証ロジック
}
}
class Database {
public function save($data) {
// データベースへの保存ロジック
}
}
■ 2. 開放閉鎖原則(OCP)
次に、開放閉鎖原則です。これは、クラスは拡張に対して開いていて、修正に対して閉じているべきだという原則です。つまり、新しい機能を追加するときは、既存のコードを変更せず拡張できるようにすることです。
Before
class Payment {
public function processPayment($amount) {
// 支払い処理のロジック
}
}
After
interface PaymentProcessor {
public function processPayment($amount);
}
class Payment implements PaymentProcessor {
public function processPayment($amount) {
// 支払い処理のロジック
}
}
class PayPalPayment implements PaymentProcessor {
public function processPayment($amount) {
// PayPalの支払い処理のロジック
}
}
■ 3. リスコフの置換原則(LSP)
次に、リスコフの置換原則です。これは、サブタイプはその基本型と置換可能でなければならないという原則です。この原則に違反すると、プログラムの予測可能性が低下し、バグや予期せぬ動作が発生する可能性を高めます。
よくあるミスは、サブクラスが基本クラスの振る舞いを正しく実装しているように見えるが、実際には基本クラスの前提条件を満たしていない場合です。
Before
class Rectangle {
protected $width;
protected $height;
public function setWidth($width) {
$this->width = $width;
}
public function setHeight($height) {
$this->height = $height;
}
public function area() {
return $this->width * $this->height;
}
}
class Square extends Rectangle {
public function setWidth($width) {
$this->width = $width;
$this->height = $width;
}
public function setHeight($height) {
$this->height = $height;
$this->width = $height;
}
}
After
interface Shape {
public function area();
}
class Rectangle implements Shape {
protected $width;
protected $height;
public function setWidth($width) {
$this->width = $width;
}
public function setHeight($height) {
$this->height = $height;
}
public function area() {
return $this->width * $this->height;
}
}
class Square implements Shape {
protected $side;
public function setSize($size) {
$this->side = $size;
}
public function area() {
return $this->side * $this->side;
}
}
■ 4. インターフェース分離の原則(ISP)
次に、インターフェース分離の原則です。これは、クライアントが利用しないメソッドに依存しないという原則です。この原則に違反すると、クライアントが不必要な依存関係を持ち、コードの複雑さや保守性が低下します。
よくあるミスは、1 つのインターフェースに複数のメソッドを含めることです。これにより、クライアントは自分が使用しないメソッドに依存しなければなりません。
Before
interface Worker {
public function work();
public function eat();
}
class Robot implements Worker {
public function work() {
// 仕事のロジック
}
public function eat() {
// ロボットは食べない
}
}
After
interface Workable {
public function work();
}
interface Feedable {
public function eat();
}
class Robot implements Workable {
public function work() {
// 仕事のロジック
}
}
■ 5. 依存性逆転の原則(DIP)
依存性逆転の原則は、高レベルのモジュールは低レベルのモジュールに依存すべきではなく、両方のモジュールは抽象に依存すべきであるという原則です。つまり、具体的な実装ではなく抽象に依存することで、柔軟性が向上し、コードの変更や拡張が容易になります。
よくあるミスは、高レベルのモジュールが低レベルのモジュールに直接依存することです。具体的な実装に依存すると、コードの変更が困難になり、柔軟性が低下します。
Before
class Database {
public function query($sql) {
// データベースクエリのロジック
}
}
class UserRepository {
private $db;
public function __construct() {
$this->db = new Database();
}
}
After
interface DBConnection {
public function query($sql);
}
class Database implements DBConnection {
public function query($sql) {
// データベースクエリのロジック
}
}
class UserRepository {
private $db;
public function __construct(DBConnection $db) {
$this->db = $db;
}
}
■ 感想
以上が、SOLID原則をよくある事例を踏まえて集めてみました。これらの原則を理解し、適切に適用することで、コードの品質と保守性を向上させることができます。
今回の内容だけで完璧なわけではなく、まだまだ沢山意識すべきところはあるのですが、まずは今回の内容を試してみて、より良いコードを書いていこうと思います!
Discussion