[OOP] 객체 지향 설계 5원칙(SOLID) : LSP(Liskov Substitution Principle)-리스코프 치환 원칙
LSP(Liskov Substitution Principle)
부모 클래스를 호출하는 동작에서 자식 클래스가 부모 클래스를 완전히 대체할 수 있다는 원칙
* 객체의 상속(다형성) ㅡ> 부모/자식 관계 형성 ㅡ> 자식 객체는 부모 객체의 특성을 가지면서 추가적 확장 가능
ㅡ> 자식 객체의 확장 과정에서 부모 객체의 의의에서 어긋나지 않게 부모 객체의 방향성을 따르는 것이 바람직하다
코드 예제
LSP가 적용되지 않은 코드
#include <iostream>
class Rectangle {
public:
virtual void setWidth(int w) { width = w; }
virtual void setHeight(int h) { height = h; }
int getWidth() const { return width; }
int getHeight() const { return height; }
int getArea() const { return width * height; }
private:
int width = 0;
int height = 0;
};
class Square : public Rectangle {
public:
void setWidth(int w) override {
Rectangle::setWidth(w);
Rectangle::setHeight(w); // 정사각형 ㅡ> 너비=높이 (필요 X)
}
void setHeight(int h) override {
Rectangle::setHeight(h);
Rectangle::setWidth(h); // 정사각형 ㅡ> 너비=높이 (필요 X)
}
};
코드를 살펴보면 직사각형(Rectangle) 클래스가 있고 해당 클래스를 정사각형(Square) 클래스가 상속하는 형태로 구현되어 있습니다. 그런데 잘 살펴보면 부모 클래스에 해당하는 직사각형 클래스에서는 높이, 너비를 변수로 가지고 있고 해당 변수들과 관계된 함수도 있지만, 자식 클래스에 해당하는 정사각형 클래스는 높이나 너비 둘 중 하나만 있으면 되기 때문에 LSP에 따라 부모 클래스의 방향성을 따르고 있다고 보기 어렵다.
LSP가 적용된 코드
#include <iostream>
class Polygon {
public:
virtual int getArea() const = 0; // 넓이를 계산 순수 가상 함수
};
class Rectangle : public Polygon {
public:
void setWidth(int w) { width = w; } //너비
void setHeight(int h) { height = h; }// 높이
int getWidth() const { return width; }
int getHeight() const { return height; }
int getArea() const override { return width * height; }
private:
int width = 0;
int height = 0;
};
class Square : public Polygon {
public:
void setSide(int s) { side = s; } //변의 길이
int getSide() const { return side; }
int getArea() const override { return side * side; }
private:
int side = 0;
};
LSP가 적용된 코드를 보면 부모 클래스에 해당하는 Polygon 클래스는 가상 함수를 통해 인터페이스만 제공하는 역할을 하고 이를 상속하여 구현된 직사각형, 정사각형 클래스들이 각 특징에 알맞은 멤버 변수들만을 가지고 넓이를 계산하는 기능도 정상 수행 가능한 것을 확인할 수 있다.
ㅡ> 부모 클래스에서 기대되는 동작이 자식 클래스에서도 보장된다. 더불어 새로운 도형이 요구될 시 부모 클래스의 코드를 수정할 필요 없이 Rectangle, Square 처럼 Polygon을 상속하는 자식 클래스로 구현만 하면 된다.
정리
LSP는 자식 객체가 부모 객체를 완전히 대체가 가능할 것을 권고한다. 만약 LSP가 적용되지 않은 코드에서 Rectangle로 선언된 포인터가 Square 객체를 가리키고 있는데 높이와 너비를 각각 5, 2로 설정하고 넓이를 계산하면 기대하는 값이 나올까? 당연하게도 기대하는 값인 5X2=10이 아니라 마지막에 설정된 2X2=4로 나올 것이고 이는 의도에서 벗어난 것이다.
그렇기 때문에 우리는 부모 클래스에서 기대되는 행동을 자식 클래스로 대체해도 문제가 없도록 설계해야만 한다.
참고자료
https://blog.itcode.dev/posts/2021/08/15/liskov-subsitution-principle
[OOP] 객체지향 5원칙(SOLID) - 리스코프 치환 원칙 LSP (Liskov Subsitution Principle) - 𝝅번째 알파카의 개
리스코프 치환 원칙은 부모 객체와 이를 상속한 자식 객체가 있을 때 부모 객체를 호출하는 동작에서 자식 객체가 부모 객체를 완전히 대체할 수 있다는 원칙이다. 객체지향 언어에선 객체의 상
blog.itcode.dev