KnowHow

技術的なメモを中心にまとめます。
検索にて調べることができます。

Python基礎 オブジェクト指向SOLID(リスコフの置換原則)

登録日 :2025/09/17 06:12
カテゴリ :Python基礎

L: Liskov Subsituation(リスコフの置換原則)

リスコフの置換原則

サブタイプは、そのスーパータイプと置換可能でなければならない。
サブタイプ:スーパークラスを継承したクラス
スーパータイプ:継承元となるクラス

正しい継承 = 「Is-a関係」+「振る舞いの同等性」

原則に違反した場合
  • 利用者が想定しない挙動によるバグが発生する可能性が高まる
     利用者はスーパータイプとサブタイプで同じ挙動を期待する。
     挙動が異なると、サブタイプまで全て理解する必要がある

  • リスコフの置換原則に違反したコードを使うと、オープンクローズドの原則に違反する可能性が高まる
     利用側でクラスを判定するための分岐を入れるのはNG

(発展)契約による設計

プログラムコードの中にプログラムが満たすべき仕様について記述することで、正確で頑強なソフトウェアとする設計技法

  • 事前条件
     メソッド開始時に保証されるべき条件
     メソッドの引数、メソッド開始時のインスタンスの状態

  • 事後条件
     メソッド正常終了時に保証されるべき条件
     メソッド正常終了時にインスタンスの状態、クライアントに返す戻り値

サブタイプの事前条件はスーパータイプと同じかそれより弱い条件と置き換え、
(スーパータイプの事前条件は、サブタイプ側で全て許容する必要がある、もっとゆるい条件とする)
事後条件はスーパータイプと同じかそれより強い条件と置き換える
(スーパータイプの事後条件も同じく、サブタイプでは全て網羅する必要がある。つまりゆるくしたらダメ)


サンプルコード

from abc import ABCMeta, abstractmethod


class IShape(metaclass=ABCMeta):
    @abstractmethod
    def get_area(self) -> int:
        pass


class Rectangle(IShape):
    def __init__(self, _height: int = 0, _width: int = 0):
        self.__height = _height
        self.__width = _width

    @property
    def width(self) -> int:
        return self.__width

    @width.setter
    def width(self, _width: int):
        self.__width = _width

    @property
    def height(self) -> int:
        return self.__height

    @height.setter
    def height(self, _height):
        self.__height = _height

    def get_area(self) -> int:
        return self.__height * self.__width


class Square(IShape):
    def __init__(self, _length: int = 0):
        self.__length = _length

    @property
    def length(self) -> int:
        return self.__length

    @length.setter
    def length(self, _length: int):
        self.__length = _length

    def get_area(self) -> int:
        return self.__length ** 2


def f(shape: IShape):
    print(shape.get_area())


if __name__ == '__main__':
    r = Rectangle(3, 4)
    f(r)

    s = Square(3)
    f(s)