티스토리 뷰

웹개발/Java

LSP 리스코프 치환의 원칙

야쿠 yaku 2010.12.30 13:37

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

 

Note

LSP는 자식 타입들은 부모 타입들이 사용되는 곳에 대체될 수 있어야 한다는 원칙이다. 즉 부모 클래스가 사용되는 곳에 자식 클래스로 치환 하더라도 문제가 없어야 한다는 의미이다.

여러가지 의미로 생각 해 볼수 있다. 상속받는 자식 클래스는 부모 클래스의 책임을 넘지 말아야 한다는 의미와 자식으로서 제공하는 기능에 대한 제약의 의미 등 제한적인 의미를 말하기도 하고, 사용상의 난해함으로 고생하지 말라는 교훈적인 의미를 말하기도 한다. 파생 클래스 마다 쓰임새가 다르다고 사용법까지 모두 다 다르다면 얼마나 복잡할 것이며, 왜 상속이라는 고수준의 구현 방법을 사용하는가?(게다가 고가의) 파생 클래스의 활용도를 높인다는 측면에서도 부모 클래스와 치환이 가능한 형태로 구현하는 것이 여러모로 효율적이다.

 

"Storage" 클래스의 파생 클래스 중 FileStorage 클래스는 저장의 기능을 SaveFile() 이라는 메소드에 구현했고, DBStorage는 Insert(), Update()라는 메소드로 구현했다고 가정하자.

 

어디에 파생 클래스로서의 메리트가 있는가? Storage라는 이름을 상속 받았다는 것에 만족해야만 하는가. 아마 이런 클래스를 가진 시스템에는 적어도 하나 이상의 "instanceof" 키워드를 사용했을 거라 감히 추측해본다. 좀더 심한 경우에는 수많은 분기문 (switch, if, else, if else ...)이 파생 클래스의 종류에 따른 메소드를 호출하기 위해 동분서주 하게 될 것이다.

 

메소드가 클래스의 의미를 분명하게 하는 이름을 가지는 것은 옳지만 이러한 경우에는 민폐도 이만 저만이 아니다. 그러므로 부모 클래스의 저장의 의미를 Save() 라는 메소드로 정의 했다면 파생 클래스에서의 저장도 Save()가 되어야 한다는 것이다. 메소드 이름이 동일하다는 것은 이름의 의미 이상의 효과를 가진다. 아래의 코드를 보자.


  1. public interface Storage{
     public void save();
    }
  2.  
  3.  public class FileStorage implements Storage{
     public void save() {System.out.println("File Save");}
    }
  4. class DBStorage implements Storage{
     public void save(){System.out.println("DB Stored");}
    }
  5.  public class Client {
     public void write(Storage storage)
     {
      storage.save();
     }

     public static void main(String[] args)
     {
      Client client = new Client();
      client.write(new FileStorage());
      client.write(new DBStorage());
     }
    }

 형광색으로 표시된 메소드는 Storage 타입의 클래스를 인자로 받아서 save라는 메소드를 호출한다. Storage 인터페이스를 구현한 FileStore와 DBStore 클래스는 부모를 잘 만난 덕에 별도의 확인 절차 없이 save 메소드가 호출될 수 있게 된 것이다. 바로 저 곳이 파생 클래스가 자유롭게 돌아다닐 수 있는 공간인 셈이다.

 

  그렇다면 파생 클래스는 부모 클래스가 제공하는 메소드와 동일한 갯수를 유지하고 이외에는 필요가 없다는 말일까?

그것은 확장과 추가의 개념을 혼동함으로써 생긴 오류다. 파생 클래스에 부모 클래스가 가지지 않는 속성이 있다면 추가되는 메소드가 생기기 마련이다. 그것은 속성의 추가이지 부모클래스로 부터의 확장은 아닌 것이다.  달리 생각 해본다면 과연 부모 클래스의 인터페이스로 뺄 수 없을 만큼 강한 성격의 속성이 과연 파생 클래스에 추가해야 만 하는 성질일까.

 

Keypoint
  • 파생 클래스는 부모 클래스와 치환되더라도 문제가 없어야 한다.

    • 부모 클래스가 제공하는 메소드 형태를 따라 기능을 제공해야 한다.
  •  파생 클래스는 확장이 주요 목적이다. 추가는 부차적인 문제이다.

lsp01.jpg 

<<4-1.  부모 타입이 사용되는 곳에 자식타입이 대체 가능한 형태>>

 

참고자료

'웹개발 > Java' 카테고리의 다른 글

알기쉬운 프로그래밍 설계 하기  (1) 2011.06.30
DIP 의존관계 역전원칙  (0) 2010.12.30
LSP 리스코프 치환의 원칙  (0) 2010.12.30
SRP 단일 책임의 원칙  (0) 2010.12.30
OCP 개방 폐쇄 원책  (0) 2010.12.30
ISP 인터페이스 분리의 원칙  (1) 2010.12.30
댓글
댓글쓰기 폼