본문 바로가기

객체지향설계 5원칙(S.O.L.I.D)

L.리스코프 치환 원칙(Liskov substitution principle)

effective-java 를 읽다 `리스코프 치환 원칙`에 대한 내용이 나왔는데 내용이 잘 생각나지 않아 정리하려 한다.

리스코프 치환 원칙이란?

컴퓨터 프로그램에서 자료형 S가 자료형 T의 하위형이라면 필요한 프로그램의 속성(정확성, 수행하는 업무 등)의 변경 없이 자료형 T의 객체를 자료형 S의 객체로 교체(치환)할 수 있어야 한다는 원칙이다. 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.

상위 타입을 전달받는 메서드 구현

public class BirdUtil {
    private BirdUtil() {
    }

    public static void fly(Bird bird) {
        bird.fly();
    }
}

상속 관계의 클래스(Bird <-- Sparrow)

public class Bird {
    public void fly() {
        System.out.println("난다.");
    }
}

public class Sparrow extends Bird {
    @Override
    public void fly() {
        System.out.println("참새가 난다");
    }
}

상위 타입을 전달받는 메서드에서 하위 타입을 전달받더라도 정상적으로 동작해야 한다.

public void fly() {
    BirdUtil.fly(new Bird());
    BirdUtil.fly(new Sparrow());
}

잘못된 상속 관계는 리스코프 치환 원칙에 위배된다.

public class Chicken extends Bird {
    @Override
    public void fly() {
        throw new IllegalStateException(); //닭은 날 수 없음
    }
}

리스코프 치환 원칙을 지키지 않았을 때 발생하는 문제점 예시

public class Rectangle {
    public Rectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }

    private int width;
    private int height;

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

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

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

public class Square extends Rectangle {
    public Square(int width, int height) {
        super(width, height);
    }

    @Override
    public void setWidth(int width) {
        super.setWidth(width);
        super.setHeight(width);
    }

    @Override
    public void setHeight(int height) {
        super.setHeight(height);
        super.setWidth(height);
    }
}

정사각형은 너비가 늘어났을 때, 높이도 늘어나게 된다.즉, 상위 타입의 정확성을 깨뜨리게 된다.

    @Test
    @DisplayName("리스코프 치환 법칙을 위배하는 예시")
    public void squareAndRectangle() {
        // given
        final Rectangle rectangle = new Rectangle(5, 5);
        final Square square = new Square(5, 5);

        // then
        assertThat(rectangle.area()).isEqualTo(square.area());

        // when
        rectangle.setWidth(10);
        square.setWidth(10);

        // then
        assertThat(rectangle.area()).isNotEqualTo(square.area());
    }

https://github.com/haedoang/solid.git