반응형
리스코프 치환 원칙(LSP)

"상위 타입의 객체를 하위 타입의 객체로 치환해도 상위 타입을 사용하는 프로그램은 정상적으로 동작해야 한다."

즉, 특정 메소드가 상위 타입을 인자로 사용한다고 할 때, 그 타입의 하위 타입도 문제없이 정상적으로 작동을 해야 한다는 것입니다.

리스코프 치환 원칙이 제대로 지켜지지 않으면 다형성에 기반한 개방 폐쇄 원칙 역시 위반하는 것이기 때문에 리스코프 치환 원칙을 지키는 것이 중요합니다.

 

C/C++ 예제
class Rectangle
{
protected:
    int width, height;
public:
    Rectangle(const int width, const int height)
        : width{ width }, height{ height } { }

    int get_width() const { return width; }
    virtual void set_width(const int width) { this->width = width; }
    int get_height() const { return height; }
    virtual void set_height(const int height) { this->height = height; }

    int area() const { return width * height; }
};

class Square : public Rectangle
{
public:
    Square(int size) : Rectangle(size, size) {}
    void set_width(const int width) override {
        this->width = height = width;
    }
    void set_height(const int height) override {
        this->height = width = height;
    }
};

void process(Rectangle& r)
{
    int w = r.get_width();
    r.set_height(10);
}

int main()
{
    Rectangle r{ 5,5 };
    process(r);

    Square s{ 5 };
    process(s);

    getchar();
    return 0;
}
결과
expected area = 50, got 50
expected area = 50, got 100

LSP를 위반하는 전형적인 예로, 너비와 높이의 조회(getter) 및 할당(setter) 메서드를 가진 직사각형 클래스로부터 정사각형 클래스를 파생하는 경우를 들 수 있다.

정사각형 클래스는 항상 너비와 높이가 같다고 간주할 수 있다.

정사각형 객체가 직사각형을 다루는 문맥에서 사용되는 경우, 정사각형의 크기는 독립적으로 변경할 수 없기 때문에 (혹은 그래서는 안되기 때문에) 예기치 못한 행동을 하게 된다. 이 문제는 고치기 쉽지 않다.

정사각형 클래스의 할당 메서드를 수정하여 정사각형의 불변 조건(즉, 너비와 높이가 같음)을 유지하면, 이 메서드는 크기를 독립적으로 변경할 수 있다고 설명한 직사각형의 할당자의 사후조건을 무력화(위반)한다.

이러한 LSP 위반은 실전에서는 LSP를 위반한 클래스를 사용하는 코드가 실제로 기대하는 사후 조건이나 불변 조건에 따라 문제가 될 수도 있고 아닐수도 있다. 여기서 중요한 사안은 가변성이다. 정사각형과 직사각형이 조회 메서드만 가진다면 (즉, 이들이 불변객체라면), LSP 위반을 발생하지 않는다. 

반응형

+ Recent posts