リスコフの置換原則(Liskov Substitution Principle)

― 誕生の背景と壊れない設計のための実践ガイド ―


1. 概要

リスコフの置換原則(LSP)は、オブジェクト指向設計の原則「SOLID」の1つです。1987年にアメリカの計算機科学者バーバラ・リスコフ(Barbara Liskov)によって提唱されました。

この原則は、「継承されたクラスが、元のクラスと同じように動作すること」を保証するための設計基準です。誤った継承によって生じる設計の破綻を防ぎます。


2. 原則の定義と意図

リスコフの置換原則(LSP)定義(Liskov, 1987)

「型Sが型Tのサブタイプであるとき、T型のオブジェクトをS型のオブジェクトで置き換えても、プログラムの意味が変わってはならない」

この定義から読み取れるのは、「親クラスを期待して書かれたコードは、子クラスでも壊れず動作しなければならない」という点です。


3. 原則が生まれた背景

3.1 オブジェクト指向の台頭

1970年代〜1980年代にかけて、構造化プログラミングの限界からオブジェクト指向の考え方が広まりました。

リスコフはこの時期に、オブジェクト指向言語「CLU」や抽象データ型(ADT)に関する研究に取り組んでおり、「継承の誤用による設計の破綻」に着目していました。

3.2 継承の乱用による問題

開発者たちは「コードの再利用」を目的に安易に継承を用い、次のような問題を起こしていました。

  • 子クラスが親クラスの契約を破って振る舞いが変わる
  • 仕様を満たさない子クラスが使われ、実行時エラーが発生
  • テストや再利用の信頼性が著しく低下

このような課題に対応するために、リスコフは「型の一貫性を保証する設計原則」としてLSPを提唱しました。


4. 壊れる設計(LSP違反例)

4.1 典型例:長方形と正方形の問題

public class Rectangle
{
    public virtual int Width { get; set; }
    public virtual int Height { get; set; }
}

public class Square : Rectangle
{
    public override int Width
    {
        set { base.Width = base.Height = value; }
    }

    public override int Height
    {
        set { base.Width = base.Height = value; }
    }
}

問題点

  • Rectangle は幅と高さを独立して設定できることが前提。
  • Square は両方を同じ値にしかできない
  • Rectangle rect = new Square(); rect.Width = 4; rect.Height = 5; のようなコードで予期せぬ結果が出る。

これは、親クラス(Rectangle)の期待される契約を子クラス(Square)が破っているため、LSP違反となります。


5. 壊れない設計(LSPを守る例)

5.1 継承をやめてインターフェースを導入

public interface IShape
{
    int Area();
}

5.2 各クラスを独立実装

public class Rectangle : IShape
{
    public int Width { get; }
    public int Height { get; }

    public Rectangle(int width, int height)
    {
        Width = width;
        Height = height;
    }

    public int Area() => Width * Height;
}

public class Square : IShape
{
    public int Side { get; }

    public Square(int side)
    {
        Side = side;
    }

    public int Area() => Side * Side;
}

5.3 使用側のコード

public class ShapePrinter
{
    public void PrintArea(IShape shape)
    {
        Console.WriteLine($"面積: {shape.Area()}");
    }
}

// 使用例
var printer = new ShapePrinter();
printer.PrintArea(new Rectangle(4, 5));  // 出力: 面積: 20
printer.PrintArea(new Square(4));       // 出力: 面積: 16

なぜ壊れないのか

  • 共通の振る舞い(Area)だけを IShape に抽象化
  • 各クラスは自分のルールで正しく実装
  • 呼び出し側は共通インターフェースだけを意識して使える

6. 設計の注意点と判断基準

継承するかのチェックリスト

質問YESなら継承OK
子クラスは親クラスの全仕様を正しく実装できるか?
子クラスが親クラスの前提(契約)を壊さないか?
子クラスが例外を投げたり、意味を変えたりしていないか?×
共通の動作だけ抽象化できないか?(インターフェースで足りないか?)

7. LSPの意義と現代での応用

SOLID原則との関係

  • LSPは「O(開放閉鎖原則)」とも密接に関係
  • 「新しい型を追加しても、既存コードが壊れない」設計を可能にする

応用場面

  • ライブラリやフレームワークのAPI設計
  • テスト可能な設計(モックを用いたテスト)
  • Unityなどゲーム開発でも、共通動作の抽象化に有効

8. まとめ

項目内容
原則名リスコフの置換原則(Liskov Substitution Principle)
提唱者Barbara Liskov(バーバラ・リスコフ)
提唱年1987年(論文「Data Abstraction and Hierarchy」)
主な動機継承による設計の破綻を防ぐ
意義継承とポリモーフィズムの信頼性向上
適用方法共通動作の抽象化、親の契約を破らない実装

訪問数 18 回, 今日の訪問数 1回