SOLID原則について

## SOLID原則とは？
プログラムを綺麗に保つための5つのお約束のこと。  
これを守ることで、プログラムがごちゃごちゃにならずに済み、後で読んだり変更したりする時に楽になる。

以下の5つの原則の頭文字をとったもの
- **S**: Single Responsibility Principle（単一責任の原則）
- **O**: Open/Closed Principle（オープン/クローズドの原則）
- **L**: Liskov Substitution Principle（リスコフの置換原則）
- **I**: Interface Segregation Principle（インターフェース分離の原則）
- **D**: Dependency Inversion Principle（依存性逆転の原則）

これらの原則を理解し適用することで、コードはより柔軟で、読みやすく、そしてメンテナンスしやすいものになる。
(これから紹介するサンプルコードはswift6)

---

## SwiftにおけるSOLID原則

### S: 単一責任の原則 (Single Responsibility Principle, SRP)
 **「1つのクラスに色々な役割を詰め込みすぎないようにしよう」**  ということ。

例えば、「レポートを作成するクラス」が「印刷」や「保存」までやるのはNG。  
それぞれの機能を別のクラスに分けることで、修正や拡張がしやすくなる。
下記はswiftを用いた良い例です。
```swift
class Report {
    func create() -> String { "This is a report" }
}

class ReportSaver {
    func save(_ report: String) { print("Saving: \(report)") }
}

class ReportPrinter {
    func print(_ report: String) { print("Printing: \(report)") }
}

let report = Report().create()
ReportSaver().save(report)
ReportPrinter().print(report)
```

---

### O: 開放閉鎖の原則 (Open/Closed Principle, OCP)
**コードを修正せずに新しい機能を追加したり、拡張できるようにしよう** という考え方。

#### 悪い例
```swift
class Report {
    func create() -> String { "This is a report" }
    
    func save(type: String) {
        let report = create()
        if type == "local" {
            print("Saving locally: \(report)")
        } else if type == "cloud" {
            print("Saving to cloud: \(report)")
        } else {
            print("Unknown type")
        }
    }
}
```
#### 良い例
```swift
class Report {
    func create() -> String { "This is a report" }
}

extension Report {
    func save() { print("Saving: \(create())") }
    func print() { print("Printing: \(create())") }
}
```
---

### L: リスコフの置換原則 (Liskov Substitution Principle, LSP)
**子クラスは親クラスと同じように動くべきというルール**
親クラスを使う場面で子クラスを入れ替えても、プログラムがちゃんと動くようにしようという考え方
#### 良い例
親クラスであるShape()には、area() メソッド(面積を計算するメソッド)だけが定義されている。

子クラスであるRectangle（長方形）と Square（正方形）はShape()に従って、それぞれの面積を計算する area() メソッド(面積を計算するメソッド)を実装している。

Shape()とRectangle()又はSquare()を入れ替えても、プログラムが正常に動作しようとする考え方。

### I: インターフェース分離の原則 (Interface Segregation Principle, ISP)
1つの大きなプロトコル、インターフェースではなく、役割ごとに小さく分けたほうがいいよね。という考え方

### D: 依存関係逆転の原則 (Dependency Inversion Principle, DIP)
**具体的な実装 (class) ではなく、抽象 (protocol) に依存**
「上のもの（重要な部分）は下のもの（細かい部分）に直接依存しないようにしよう」 という考え方です。
#### 悪い例
```swift
class Phone {
    func call() {
        print("電話をかける")
    }
}

class PhoneApp {
    var phone: Phone

    init(phone: Phone) {
        self.phone = phone
    }

    func makeCall() {
        phone.call()  // Phoneに依存している
    }
}
```
#### 良い例
```swift
// 抽象クラス（プロトコル）
protocol Caller {
    func call()
}

// 具体的な実装（電話をかける方法）
class Phone: Caller {
    func call() {
        print("電話をかける")
    }
}

// 具体的な実装（Skypeで電話をかける方法）
class Skype: Caller {
    func call() {
        print("Skypeで電話をかける")
    }
}

// アプリは抽象（Caller）に依存
class PhoneApp {
    var caller: any Caller

    init(caller: any Caller) {
        self.caller = caller
    }

    func makeCall() {
        caller.call()  // 抽象（Caller）に依存している
    }
}
```
# SOLID原則 まとめ表

| 原則 | 概要 | 例 |
|------|------|----|
| **単一責任の原則 (SRP)** | 1つのクラスには1つの責務だけを持たせる | レポート作成、保存、印刷をそれぞれ別のクラスに分ける |
| **開放閉鎖の原則 (OCP)** | 既存のコードを変更せずに、新しい機能を追加できるようにする | `if-else` を使わず、拡張 (`extension`) で機能を追加 |
| **リスコフの置換原則 (LSP)** | 子クラスは親クラスと置き換え可能であるべき | `Shape` クラスを継承する `Rectangle` や `Square` が、適切に `area()` を実装 |
| **インターフェース分離の原則 (ISP)** | 大きなインターフェースを小さく分割し、不要な依存を避ける | 1つの巨大なプロトコルではなく、必要な機能ごとに分割 |
| **依存関係逆転の原則 (DIP)** | 具体的なクラスではなく、抽象 (`protocol`) に依存する | `Caller` プロトコルを定義し、`Phone` や `Skype` のような具体的な実装と分離 |

これらを守ることで、コードの可読性・再利用性が向上し、変更にも強い設計が可能になる。