LSP(Liskov Substitution Principle)
LSP?
LSP는 리스코프 치환 원칙(Liskov Substitution Principle) 의 약자로 서브 타입은 언제나 부모타입으로 교체 가능해야함을 뜻한다.
즉 자식 클래스에서 최소한 자신의 부모 클래스에서 가능한 행위의 수행이 보장되어야함을 의미한다.
예를 들어, 변수에 LinkedList 자료형을 담아 사용하다 중간에 전혀 다른 HashSet 자료형으로 바꿔도 Collection의 추상 메서드를 각 클래스에서 인터페이스 구현 규약을 잘 지키도록 implements 되어 있기 때문에 add()의 메서드 동작이 보장받는다.
목적
다형성을 지원하기 위한 원칙
이는 곧 자식 클래스의 구현 원칙, 행동 규약을 의미한다.
상위 클래스 변수로 하위 클래스 인스턴스를 받았을 때,
부모의 메서드를 사용해도 동작이 의도대로만 흘러가도록 구성해야한다는 점이다.
자식 메서드에서 잘못된 오버로딩, 오버라이딩을 해 부모 메서드와 어긋나는 기능을 갖게 된다면
기존 부모 클래스를 사용하는 코드에서 예상치 못한 결과를 야기할 수 있다.
예제
abstract class Animal{
void speak() {}
}
class Cat extends Animal{
void spaek() {
System.out.println("냐옹");
}
}
class Dog extends Animal{
void speak() {
System.out.println("멍멍");
}
}
class Fish extends Animal{
void speak() {
try {
throw new Exception("물고기는 말할 수 없음") ;
}catch(Exception e) {
e.printStackTrace();
}
}
}
public class LSP {
public static void main(String[] args) {
List<Animal> list = new ArrayList<>();
list.add(new Cat());
list.add(new Dog());
list.add(new Fish());
for(Animal a : list) {
a.speak(); // java.lang.Exception: 물고기는 말할 수 없음
}
}
}
문제
LSP 원칙에 따르면 speak 메서드 실행 시 Animal 클래스의 설계 목적에 따라
‘각 동물 타입에 맞게 울부짖는 결과’ 를 내야하는데 갑자기 예외를 던진다.
해결
‘말하는 기능’ 자체를 인터페이스로 분리한다
abstract class Animal{}
interface Speakable{
void speak() ;
}
class Cat extends Animal implements Speakable{
public void speak() {
System.out.println("냐옹");
}
}
class Dog extends Animal implements Speakable{
public void speak() {
System.out.println("멍멍");
}
}
class Fish extends Animal{}
결론
LSP 원칙의 핵심은 ‘상속’으로, 상속은 IS-A 관계가 있을 경우로만 제한되어야 한다.
그 외의 경우는 ‘합성’을 이용한다.
실제로 하위 클래스에서 상위 클래스의 메서드를 재정의 해야하는 경우는 굉장히 많으므로,
불필요하게 상속 관계를 이용하는것이 아닌 인터페이스로 구현하여 이용한다.
참고 사이트
💠 완벽하게 이해하는 LSP (리스코프 치환 원칙) (tistory.com)
댓글남기기