インターフェース分離の法則(ISP)

ISP

インターフェース分離の法則はインターフェースはシンプルに実装しましょうという原則です。

違反例として乗り物(Vehicle)インターフェースを作成してみました。

protocol Vehicle {
    func fly()
    func swim()
    func run()
}

Vehicleに準拠したオブジェクト、例えば飛行機クラスを作成しようとした際flyとrunメソッドには何らかの処理が入りそうですが、一般的な飛行機は泳ぐ機能を必要としないためswimメソッドが必要ありません。しかしprotocolに記述されているため実装しないとエラーになってしまいます。

ISPに違反した実装

飛行機クラス(Airplane)をISPに違反した形で作成してみます。

コード

swim()メソッドを実装していますが飛行機は泳がないので処理がなく空になっています。

この様な間違った実装をしてしまうとAirplaneを使った際swim()を呼び出して泳ぐことができないバグだ!と誤解してしまいます。

protocol Vehicle {
    func fly()
    func swim()
    func run()
}

class Airplane: Vehicle {
    func fly() {
        print("⊂二二二( ^ω^)二⊃ ブーン")
    }
    
    func swim() {
        // このメソッドは呼び出さないでください
    }
    
    func run() {
        // このメソッドは呼び出さないでください
    }
}

ISPを守った実装

乗り物は全て移動する為のものだと抽象的に考えVehicleプロトコルの中身をそれぞれのProtocolとして実装しました。

この様な実装で不必要な依存関係ををなくすのがISPの原則です。飛行機に加えて車と潜水艦も実装しましたが不必要な依存関係がないのがわかります。

protocol Fly {
    func fly()
}
protocol Run {
    func run()
}
protocol Swim {
    func swim()
}

class Airplane: Fly {
    
    func fly() {
        print("⊂二二二( ^ω^)二⊃ ブーン")
    }
}

class Car: Run {
    func run() {
        print("🚗💨")
    }
}

class Submarine: Swim {
    func swim() {
        print("水深100m!!全速前進")
    }
}

もしRunとSwim両方兼ね備えた水陸両用車を作成したい場合はRunとSwimプロトコルを両方をオブジェクトに準拠させても良いですし、水陸両用車特有の追加機能が必要なのであれば以下の様にあたらにprotocolを作成し実装するのが良いでしょう 。

protocol AmphibiousCar: Run, Swim {
    //追加したい処理
}

class SpecialCar: AmphibiousCar {
    func run() {
        print("🚗💨")
    }
    
    func swim() {
        print("水深100m!!全速前進")
    }
}

参考文献

オブジェクト指向設計原則とは

開発者が知っておくべきSOLIDの原則