리스코프 치환 원칙(Liskov Substitution Principle, LSP)은 객체 지향 프로그래밍에서 중요한 원칙 중 하나로, 이 원칙은 Barbara Liskov에 의해 제안되었습니다. LSP의 핵심 아이디어는 "하위 클래스의 인스턴스는 상위 클래스의 인스턴스로 대체될 수 있어야 한다(즉, 치환될 수 있어야 한다)"는 것입니다.
간단한 예를 통해 LSP를 살펴보겠습니다.
잘못된 예:
직사각형(Rectangle)과 정사각형(Square) 클래스를 생각해보세요. 많은 사람들이 정사각형은 직사각형의 특별한 경우로 생각할 수 있기 때문에, 정사각형을 직사각형의 하위 클래스로 구현하려고 할 수 있습니다.
class Rectangle:
def __init__(self, width, height):
self._width = width
self._height = height
def set_width(self, width):
self._width = width
def set_height(self, height):
self._height = height
def area(self):
return self._width * self._height
class Square(Rectangle):
def __init__(self, side_length):
super().__init__(side_length, side_length)
def set_width(self, width):
super().set_width(width)
super().set_height(width)
def set_height(self, height):
super().set_width(height)
super().set_height(height)
위의 예에서 Square
는 Rectangle
의 하위 클래스입니다. 하지만 문제가 발생합니다. 만약에 우리가 직사각형의 너비와 높이를 독립적으로 변경할 수 있다고 가정했을 때, 정사각형의 경우에는 높이와 너비가 항상 같아야 합니다. 따라서 Square
클래스에서 높이나 너비를 변경하면 다른 하나도 같이 변경되어야 합니다.
이렇게 구현하면 Rectangle
의 인스턴스로 Square
의 인스턴스를 대체할 때 예상하지 못한 문제가 발생할 수 있습니다. 이는 LSP를 위반하는 것입니다.
올바른 예:
직사각형과 정사각형은 별도의 클래스로 처리하되, 공통의 인터페이스나 상위 클래스를 갖게 하는 것이 더 적절합니다.
class Shape:
def area(self):
pass
class Rectangle(Shape):
# ... (앞서의 구현과 같음)
class Square(Shape):
def __init__(self, side_length):
self._side_length = side_length
def area(self):
return self._side_length * self._side_length
이렇게 구현하면 각각의 특성에 맞게 독립적인 동작을 수행할 수 있으며, LSP를 위반하지 않습니다.