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?

【Clean Architecture】LSP: Liskov Substitution Principle(リスコフの置換原則)

Last updated at Posted at 2025-09-29

はじめに

SOLID 原則の 3 つ目は LSP(リスコフの置換原則)
これはオブジェクト指向設計の「継承」にまつわる重要な考え方で、派生クラスが基底クラスの契約を壊さないこと を求めます。


1. 定義

リスコフの置換原則(LSP) とは:

派生クラスは、基底クラスとして置き換えても動作が破綻しないべきである。

  • 基底クラスのインスタンスを使う場面で、派生クラスを差し替えても正常に動作する
  • 「IS-A」の関係を乱用すると違反が起こりやすい

2. 直感的な例

LSP 違反

open class Bird {
    open fun fly() = println("Flying...")
}

class Sparrow : Bird()

class Penguin : Bird() {
    override fun fly() {
        // ペンギンは飛べない → 例外を投げる
        throw UnsupportedOperationException("Penguins can't fly!")
    }
}
  • Bird を前提に fly() を呼ぶと、ペンギンだけエラーになる
  • これは「ペンギンは鳥だから継承で表す」とした設計が LSP違反 になる例

LSP 準拠

interface Flyable {
    fun fly()
}

open class Bird

class Sparrow : Bird(), Flyable {
    override fun fly() = println("Flying...")
}

class Penguin : Bird() {
    fun swim() = println("Swimming...")
}
  • 飛ぶ能力(Flyable)鳥(Bird) を分離
  • Penguin を使っても Bird の契約は壊れない

3. Flutter / Android での例

Flutter

abstract class MediaPlayer {
  void play();
}

class AudioPlayer implements MediaPlayer {
  @override
  void play() => print("Playing audio...");
}

class VideoPlayer implements MediaPlayer {
  @override
  void play() => print("Playing video...");
}

// LSP違反例
class ImageViewer implements MediaPlayer {
  @override
  void play() => throw UnsupportedError("Image cannot be played!");
}

MediaPlayerImageViewer を継承させたのは誤り。
→ 解決策:Displayable など別インターフェースを用意する。


Android (Kotlin)

interface Authenticator {
    fun authenticate(): Boolean
}

class PasswordAuth : Authenticator {
    override fun authenticate() = true
}

class FingerprintAuth : Authenticator {
    override fun authenticate() = true
}

// LSP違反例
class GuestUser : Authenticator {
    override fun authenticate() = throw UnsupportedOperationException()
}

GuestUser は認証の概念自体を持たないため Authenticator を実装すべきではない。


4. LSP 違反が招く問題

  • 実行時エラーが頻発(UnsupportedOperationException など)
  • APIの信頼性が低下(契約が守られていない)
  • テスト容易性が下がる(モック/スタブで差し替えられない)
  • クライアントコードが複雑化(型チェックや例外処理が増える)

5. 実務での適用ポイント

  • 「is-a」ではなく「can-do」で考える
    → 「ペンギンは鳥」より「ペンギンは泳げる」
  • 継承よりコンポジションを優先
    → 能力(interface)を組み合わせて表現する
  • 契約違反を起こす可能性があるなら、別インターフェースに分ける
  • レビュー時に質問する
    → 「この派生クラスは、基底クラスとして安全に置き換えられますか?」

6. まとめ

  • LSP = 継承の契約を壊さないこと
  • 違反は「IS-Aの乱用」で起こりやすい
  • 解決策は インターフェース分離コンポジションの活用
  • LSPを守ると APIの一貫性・テスト容易性・拡張性 が大幅に改善する

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?