[SOLID] 리스코프 치환 원칙(LSP)을 적용하지 않은 샘플과 적용한 샘플 비교

엔스마트·2024년 7월 1일

리스코프 치환 원칙(Liskov Substitution Principle, LSP)은 자식 클래스가 부모 클래스의 기능을 모두 대체할 수 있어야 한다는 원칙입니다. 즉, 부모 클래스 객체를 자식 클래스 객체로 대체해도 프로그램의 기능이 정상적으로 작동해야 합니다. 이를 통해 상속 관계에서 일관성과 예측 가능성을 유지할 수 있습니다.

리스코프 치환 원칙을 적용하지 않은 샘플

아래 예제에서는 직사각형(Rectangle)과 정사각형(Square) 클래스를 사용하여 LSP를 적용하지 않은 경우와 적용한 경우를 보여드립니다.

Rectangle.java

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

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

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

    public int getWidth() {
        return width;
    }

    public int getHeight() {
        return height;
    }

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

Square.java (LSP 미적용)

public class Square extends Rectangle {

    @Override
    public void setWidth(int width) {
        this.width = width;
        this.height = width; // 정사각형이므로, 높이와 너비가 같아야 한다.
    }

    @Override
    public void setHeight(int height) {
        this.width = height;
        this.height = height; // 정사각형이므로, 높이와 너비가 같아야 한다.
    }
}

Main.java (LSP 미적용 테스트)

public class Main {
    public static void main(String[] args) {
        Rectangle rect = new Rectangle();
        rect.setWidth(5);
        rect.setHeight(10);
        System.out.println("Rectangle area: " + rect.getArea()); // 예상: 50

        Rectangle square = new Square();
        square.setWidth(5);
        square.setHeight(10);
        System.out.println("Square area: " + square.getArea()); // 예상: 50, 실제: 100
    }
}

위 코드에서 Square 클래스는 Rectangle 클래스를 상속받았지만, LSP를 위반하고 있습니다. 정사각형 객체(Square)가 직사각형 객체(Rectangle)로 대체되었을 때 예상한 결과와 실제 결과가 다릅니다.

리스코프 치환 원칙을 적용한 샘플

LSP를 준수하려면 Rectangle 클래스와 Square 클래스를 별도로 정의하거나, 상속을 사용하지 않고 인터페이스를 사용하는 방법을 고려할 수 있습니다.

Shape.java (인터페이스 사용)

public interface Shape {
    int getArea();
}

Rectangle.java

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 int getHeight() {
        return height;
    }

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

Square.java

public class Square implements Shape {
    private int side;

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

    public int getSide() {
        return side;
    }

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

Main.java (LSP 적용 테스트)

public class Main {
    public static void main(String[] args) {
        Shape rect = new Rectangle(5, 10);
        System.out.println("Rectangle area: " + rect.getArea()); // 예상: 50

        Shape square = new Square(5);
        System.out.println("Square area: " + square.getArea()); // 예상: 25
    }
}

비교 분석

LSP 미적용 코드의 문제점:

  • 예상 결과와 실제 결과 불일치: Square 클래스가 Rectangle 클래스를 상속받아 대체되었을 때 예상한 면적 값과 실제 면적 값이 다릅니다. 이는 LSP를 위반하여, 클래스의 대체 가능성을 해칩니다.
  • 코드 유지보수 어려움: 상속 구조가 잘못 설계되어 유지보수와 확장이 어려워집니다.

LSP 적용 코드의 장점:

  • 예상 결과와 실제 결과 일치: Rectangle과 Square 클래스가 Shape 인터페이스를 구현하므로, 각 클래스는 자신의 방식대로 면적을 계산합니다. 이를 통해 LSP를 준수하고, 클래스 대체 가능성이 보장됩니다.
  • 코드 유지보수 용이성: 상속 대신 인터페이스를 사용하여, 각 클래스가 독립적으로 동작하고 유지보수와 확장이 용이합니다.

결론

리스코프 치환 원칙을 준수하면 상속 구조에서 자식 클래스가 부모 클래스를 대체할 수 있어, 코드의 일관성과 예측 가능성을 유지할 수 있습니다. 이를 통해 소프트웨어의 유지보수성과 확장성을 향상시킬 수 있습니다. 따라서 소프트웨어 개발에서 LSP를 준수하는 것이 중요합니다.

profile
클라우드 전환, MSA 서비스, DevOps 환경 구축과 기술지원 그리고 엔터프라이즈 시스템을 구축하는 최고 실력과 경험을 가진 Architect Group 입니다.

0개의 댓글