0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SOLID原則について

Last updated at Posted at 2025-03-31

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を用いた良い例です。

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)

コードを修正せずに新しい機能を追加したり、拡張できるようにしよう という考え方。

悪い例

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")
        }
    }
}

良い例

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) に依存
「上のもの(重要な部分)は下のもの(細かい部分)に直接依存しないようにしよう」 という考え方です。

悪い例

class Phone {
    func call() {
        print("電話をかける")
    }
}

class PhoneApp {
    var phone: Phone

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

    func makeCall() {
        phone.call()  // Phoneに依存している
    }
}

良い例

// 抽象クラス(プロトコル)
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 クラスを継承する RectangleSquare が、適切に area() を実装
インターフェース分離の原則 (ISP) 大きなインターフェースを小さく分割し、不要な依存を避ける 1つの巨大なプロトコルではなく、必要な機能ごとに分割
依存関係逆転の原則 (DIP) 具体的なクラスではなく、抽象 (protocol) に依存する Caller プロトコルを定義し、PhoneSkype のような具体的な実装と分離

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

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?