LSP(Liskov Substitution Principle)이란?


리스코프 치환 원칙(영어: Liskov substitution principle, LSP)은 바바라 리스코프가 자료 추상화와 계층 (Data abstraction and hierarchy)이라는 제목으로 기조연설을 한 1987년 컨퍼런스에서 처음 소개한 내용이다

 

아래는 원문의 일부분이다.

Barbara Liskov wrote LSP in 1988:
What is wanted here is something like the following substitution property: If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T." - BarbaraLiskov, Data Abstraction and Hierarchy, SIGPLAN Notices, 23,5 (May, 1988).

원문의 해석이 힘들다면 갓파고나 구글번역기를 이용하길 추천한다. 절대 필자가 해석하기 귀찮거나 영어를 못해서가 아니다. 크흠;;

 

위의 내용을 단순하게 풀어보자면 LSP는 일반화 관계에 대한 이야기이고 자식 클래스는 최소한 자신의 부모 클래스에서 가능한 행위는 수행할 수 있어야 한다는 뜻이다.

LSP를 만족한다면 프로그램에서 부모 클래스의 인스턴스 대신에 자식 클래스의 인스턴스로 대체해도 프로그램의 의미는 변화되지 않는다.

 

좀 더 한줄로 정리해보자면 하위 클래스는 수퍼 클래스로 대체 가능해야합니다라고 말할 수 있겠다.

 

 

 

리스코프 치환 원칙이 왜 중요한가?


1. LSP 원칙을 지키지 않으면 서브클래스 인스턴스를 파라미터로 전달 했을 때 메소드가 이상하게 작동 할 수 있습니다.

 

2. 슈퍼클래스에 대해 작성된 단위테스트가 서브클래스에 대해서는 작동되지 않을 것입니다.

 

 

 

리스코프 치환 원칙이 위배된 예제


대표적인 예제로 사각형예제를 들 수 있습니다.

아래는 높이와 너비를 가지는 Rectangle 클래스 입니다.

class Rectangle {
    private int width;
    private int height;

    public void setHeight(int height) {
        this.height = height;
    }

    public int getHeight() {
        return this.height;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getWidth() {
        return this.width;
    }

    public int area() {
        return this.width * this.height;
    }
}

 

아래는 위의 Rectangle을 상속받고 있는 Square라는 클래스입니다.

setHight(int value)setWidth(int value)Override하였습니다.

class Square extends Rectangle {
    @Override
    public void setHeight(int value) {
        this.width = value;
        this.height = value;
    }

    @Override
    public void setWidth(int value) {
        this.width = value;
        this.height = value;
    }
}

 

아래는 이해를 돕기위한 test코드입니다.

너비9, 높이3으로 설정한 뒤 넓이를 구하고 확인하는 함수입니다.

class TestLSP {
    static boolean check(Rectangle r) {
        r.setWidth(9);
        r.setHeight(3);

        if(r.area() != 27 ){ // Error Size
            return false;
        }

        return true;
    }
    public static void main(String[] args){
        Test.check(new Rectangle()); //  true
        Test.check(new Square()); // false
    }
}

 

부모 클래스인 Rectangle은 결과 값을 true를 확인 할 수 있지만 Squarefalse로 확인 할 수 있습니다.

Square의 area()리턴값은 9가 나올 것 입니다.

자식 클래스 Square는 부모 클래스 Rectanglearea() 기능을 제대로 하지 못하고 있기 때문에 LSP를 위반한 하나의 예제로 볼 수 있습니다.

 

해결 방법


상속의 관계를 제거하는 방법.

기능을 제대로 하지 못하는 area()를 자식 클래스로 이동시키는 방법.

 

위 코드와 같은 문제가 발생하는 것을 막는 간단한 방법은 재정의 @Override를 하지 않는것이다.

 

 

 

참고한 자료

https://ko.wikipedia.org/wiki/%EB%A6%AC%EC%8A%A4%EC%BD%94%ED%94%84_%EC%B9%98%ED%99%98_%EC%9B%90%EC%B9%99 
https://code.tutsplus.com/tutorials/solid-part-3-liskov-substitution-interface-segregation-principles--net-36710
책 JAVA 객체 지향 디자인 패턴(UML과 GoF 디자인 패턴 핵심 10가지로 배우는), 정인상, 채홍석 지음, 한빛미디어

개방-폐쇄 원칙 (OCP, Open-Closed Principle)이란?

 

소프트웨어 개체(클래스, 모듈, 함수 등등)는 확장에 대해 열려 있어야 하고, 수정에 대해서는 닫혀 있어야 한다'는 프로그래밍 원칙이다.

 

다시 말하자면 변경을 위한 비용은 가능한 줄이고 확장을 위한 비용은 가능한 극대화 해야 한다는 의미로, 요구사항의 변경이나 추가사항이 발생하더라도, 기존 구성요소는 수정이 일어나지 말아야 하며, 기존 구성요소를 쉽게 확장해서 재사용할 수 있어야 한다는 뜻이다.

 


 

개방-폐쇄 원칙의 두 가지 속성

 

1.확장에 대해 열려 있다.

 

이것은 모듈의 동작을 확장할 수 있다는 것을 의미한다. 애플리케이션의 요구 사항이 변경될 때, 이 변경에 맞게 새로운 동작을 추가해 모듈을 확장할 수 있다. , 모듈이 하는 일을 변경할 수 있다.

 

2.수정에 대해 닫혀 있다

 

모듈의 소스 코드나 바이너리 코드를 수정하지 않아도 모듈의 기능을 확장하거나 변경할 수 있다. 그 모듈의 실행 가능한 바이너리 형태나 링크 가능한 라이브러리(예를 들어 윈도의 DLL이나 자바의 .jar)를 건드릴 필요가 없다.

 


 

개방-폐쇄 원칙 해결방법

 

 추상화상속을 통해 지켜질 수 있다.

 

예시

개방 폐쇠 원칙에 위반된 설계

 

일단 위 그림은 OCP를 위반한다.

성적표와 출석부를 출력하는 기능을 클라이언트 클래스가 사용하도록 할 수 있다.

이때 만약 새로운 기능인 도서 대여기록 출력을 추가하면 클라이언트 클래스를 다시 수정해야하기 때문에 OCP를 위반한다.

 

 

아래의 그림은 OCP에 위배되지 않는 설계의 그림이다.

 

개방 폐쇠 원칙을 지킨 설계

 

 

새로운 출력을 표현하는 클래스를 추가하게 하고, 이러한 변경이 있더라도 기존의 클래스가 영향을 받지 않게 클라이언트 클래스가 개별적인 클래스를 처리하지 않도록 하고, 인터페이스에서 구체적인 출력 매체를 캡슐화해 처리하도록 한다.

+ Recent posts