OOP / / 2024. 3. 10. 15:30

리스코프 치환 원칙(Liskov Substitution Principle, LSP)

설명

  • 상속 관계에 있는 클래스들이 서로를 대체 할 수 있다는 원칙입니다.
  • S가 T의 하위 타입이라면 T 타입의 객체를 S 타입의 객체로 대체해도 프로그램의 동작이 변경되지 않아야 합니다.
  • 서브 클래스는 슈퍼 클래스의 기능을 변경하지 않고 확장하여야 하며, 이를 통해 프로그램의 일관성을 유지하고 안정성을 확보할 수 있습니다.

 

Bad Case

public class Rectangle {
    protected int width;
    protected int height;

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

    public int getWidth() {
        return width;
    }

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

    public int getHeight() {
        return height;
    }

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

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

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

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

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

 

Good Case

public interface Shape {
    int getArea();
}

public class Rectangle implements Shape {
    protected int width;
    protected int height;

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

    public int getWidth() {
        return width;
    }

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

    public int getHeight() {
        return height;
    }

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

    @Override
    public int getArea() {
        return width * height;
    }
}

public class Square implements Shape {
    protected int side;

    public Square(int side) {
        this.side = side;
    }

    public int getSide() {
        return side;
    }

    public void setSide(int side) {
        this.side = side;
    }

    @Override
    public int getArea() {
        return side * side;
    }
}

 

Why?

Bad Case에서, Square 클래스는 Rectangle 클래스를 상속받습니다. 하지만 정사각형은 모든 변의 길이가 같아야 하는 특성 때문에, setWidth와 setHeight 메서드에서 둘 다 같은 값으로 설정하는 방식으로 오버라이딩 됩니다. 이로 인해, Rectangle의 인스턴스로 예상되는 동작과 Square 인스턴스의 실제 동작 사이에 불일치가 발생합니다. 예를 들어, Rectangle의 가로와 세로를 독립적으로 변경하는 것이 가능하지만, Square에서는 이러한 변경이 서로에게 영향을 미칩니다. 이는 LSP를 위반하는 것이며, Square가 Rectangle의 자리를 대체할 수 없음을 의미합니다.

Good Case에서, Rectangle과 Square는 Shape 인터페이스를 구현합니다. 이로써 각각의 클래스는 getArea 메서드를 통해 자신의 면적을 계산하는 동작을 제공합니다. 이 설계는 Rectangle과 Square가 서로 다른 타입임을 명확히 하며, 각각 독립적으로 확장될 수 있도록 합니다. 더 중요한 것은, 이 방식은 LSP 원칙을 준수합니다. Shape 인터페이스를 사용함으로써, 우리는 어떤 Shape 구현체가 주어지든 그것의 면적을 계산할 수 있으며, 이는 프로그램의 다른 부분에서 해당 인스턴스를 사용할 때 예상되는 동작을 변경하지 않습니다.

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유