티스토리 뷰

SOLID 원칙 중 L에 해당하는 리스코프 치환 원칙(Liskov Substitution Principle, LSP)은 "서브타입은 언제나 기본 타입으로 교체될 수 있어야 한다"는 원칙입니다. 다르게 말하면, 클래스 A의 객체를 클래스 B의 객체로 대체하였을 때, 프로그램이 제대로 작동해야 한다면 클래스 B는 클래스 A의 서브타입이라고 할 수 있습니다.

이 원칙을 준수하면 다형성을 이용한 객체지향 프로그래밍이 잘 동작하게 됩니다.

예제

문제 상황:

동물을 나타내는 Animal 클래스와 그 하위 클래스인 Bird 클래스가 있다고 합시다. BirdAnimal을 상속받고 fly 메서드를 추가로 가지고 있습니다. 여기서 Penguin이라는 특별한 종류의 Bird를 표현하고 싶다면 어떻게 해야 할까요?

class Animal {
    void eat() {
        System.out.println("Eating...");
    }
}

class Bird extends Animal {
    void fly() {
        System.out.println("Flying...");
    }
}

class Penguin extends Bird {
    @Override
    void fly() {
        throw new UnsupportedOperationException("Penguins can't fly");
    }
}

위의 코드에서는 PenguinBird를 상속받기 때문에 fly 메서드도 상속받게 됩니다. 그런데 펭귄은 실제로 날 수 없기 때문에 fly 메서드를 오버라이드하여 예외를 발생시키고 있습니다. 이것은 리스코프 치환 원칙에 위반됩니다. 왜냐하면 Bird 타입의 객체를 Penguin 객체로 치환하였을 때 프로그램이 제대로 작동하지 않기 때문입니다.

해결 방법:

BirdPenguin의 관계를 재정의하고, 행동(비행)을 인터페이스로 분리하여 문제를 해결할 수 있습니다.

class Animal {
    void eat() {
        System.out.println("Eating...");
    }
}

interface Flyable {
    void fly();
}

class Sparrow extends Animal implements Flyable {
    @Override
    public void fly() {
        System.out.println("Flying...");
    }
}

class Penguin extends Animal {
    // Penguins don't implement Flyable because they can't fly
}

이렇게 변경하면 Penguin은 비행 기능을 갖고 있지 않기 때문에 Flyable 인터페이스를 구현하지 않고, 비행을 할 수 있는 새는 Flyable 인터페이스를 구현하게 됩니다. 이 방식은 리스코프 치환 원칙을 준수하면서도 각 동물의 실제 특성을 정확하게 나타낼 수 있습니다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크