인터페이스와 폴리모피즘




인터페이스는 약속을 정해 놓은 것과 같습니다. 리모트 콘트롤에서 볼륨을 높이는

화살표를 누르면 TV의 볼륨이 높아지는 것과 같이 인터페이스와 인터페이스를 구현한

객체가 서로 결합되었을 때 인터페이스의  메소드를 호출하면 인터페이스를 구현한 객체가

인터페이스가 호출한 메소드와 이름이 같은 자신이 구현한 메소드를 실행합니다.



 

 

             Instrument i = new Piano();

             i.sound();

예제 4 - 11

 

예제 4 - 11은 이와 같은 과정을 잘 설명해주고 있는데 프로그래머는 Piano객체를 만들고

Piano객체와 Piano 레퍼런스를 결합 시킨게 아니라 Instrument 레퍼런스를 결합

시켰습니다. Piano객체와 Piano레퍼런스만 결합할 수 있는 것이 아니라 형태가 다른

Instrument 와 결합할 수 있는 성질을 폴리모피즘(다형성)이라고 합니다.

자바에서는 인터페이스와 인터페이스를 구현한 객체와의 결합을 아주 적극적으로 지원

합니다.

일반적인 클래스는 객체와 레퍼런스를 결합할 때 = 왼편에는 객체와 같은 클래스의

레퍼런스를 두고 = 오른편에는 new 생성자를 이용해 객체를 만듭니다.

인터페이스를 구현한 클래스 객체는 한가지 옵션이 더 있는데 = 왼편의 레퍼런스 부분을

자신의 클래스형으로 해도 되지만 인터페이스 형으로 해도 무방합니다.

 왜냐하면 그 자신이 인터페이스를 구현했기 때문입니다. 인터페이스에서 선언한 모든

메소드를 자신도 가지고 있기 때문에 인터페이스형 레퍼런스가 호출할 수 있는 모든

메소드를 호출해도 감당할 수 있습니다.

 

논리적으로는 피아노는 피아노 이기도 하지만 그와 동시에 악기이기도 한 이유입니다.

다만 객체의 레퍼런스로 인터페이스 형으로 정했을 때는 그 객체가 가지고 있는 모든

메소드를 호출할 수 있는 것이 아니라 인터페이스에서 선언한 메소드만을 호출할 수

있을 뿐입니다. 일반적으로 인터페이스를 구현한 클래스 객체는 적어도 인터페이스에서

선언한 메소드와 더불어 추가의 메소드 몇 개를 더 가지고 있을 수 있으니까요.

 

예제 4 - 11에서 Instrument 레퍼런스를 통해서 i.sound()를 호출하게 되면 Piano객체는

자신의 멤버메소드 sound()메소드를 실행합니다.

실제 객체를 제어하는 Instrument 인터페이스 레퍼런스 i는 호출할 수 있는 메소드가

오직 public void sound()뿐입니다. i.sound()를 호출하면 그 결과가 일정하지 않고

Instrument 인터페이스를 구현한 객체가 나름대로 sound()메소드를 구현했을텐데,

그 나름대로 구현한 sound()메소드의 내용이 수행됩니다.

그러니까 컴파일할때는 i.sound()의 결과로 어떤 내용이 출력될 것인지에 대해서 전혀

알 수 없습니다. 프로그램이 실행이 되어야만, Instrument 인터페이스에 어떤 구체적

객체가 바인딩 되었는냐에 따라 결과가 달라집니다.

메소드 오버로딩을 이용한 것과는 사뭇다릅니다.

public void play(Violin v) 등의 메소드 였다면 public void play(Violin v)가 호출되면

바이올린이 연주되는 것을 알 수 있고, public void play(Flute f)의 메소드가 호출되면

플룻이 연주된다고 짐작할 수 있지만 public void play(Instrument i)의 경우는 메소드가

호출 되더라도 어떤 악기가 연주 될 지 짐작 할 수 없습니다.

Instrument 인자와 결합하는 데이터형은 Violin, Guitar, Flute,Piano등 어떤 것이 될지

모릅니다. GoodPlayer클래스는 Violin,Guitar,Flute,Piano등의 클래스에 대해서 알지도 못합

니다. 알지도 못한다는 뜻은 Violin,Guitar,Flute,Piano등의 클래스 파일이 없어도 컴파일이

잘 된다는 뜻입니다. , 컴파일 할 때는 GoodPlayer 클래스는 연주악기 클래스에 대한

정보를 전혀 몰라도 상관이 없다는 뜻입니다.

다만 아는 것은 Instrument일 뿐이지요. 단지 GoodPlayer클래스는 Instrument 데이터형은

sound()라는 메소드를 호출 할 수 있다는 것만 알고 있을 뿐입니다.

이것은 프로그램에 상당한 유연성을 주는 기능을 합니다.

GoodPlayer클래스를 컴파일 할 때 구체적인 연주 악기에 대한 정보가 없어도 된다는 뜻

은 추후에 PanFlute라는 연주악기를 추가하더라도 PanFlute Instrument이기만 한다면

GoodPlayer클래스는 연주할 수 있다는 겁니다.

Player의 클래스는 PanFlute를 연주할 수 있는 것입니다.

 

             GoodPlayer p = new GoodPlayer();

             p.play(new PanFlute());

예제 4 - 12

 

Player클래스와 GoodPlayer클래스를 비교해보면 Player클래스는 메소드 오버로딩을 이용한

설계이고 GoodPlayer클래스는 폴리모피즘을 이용한 설계입니다.

폴리모피즘을 이용한 설계가 메소드 오버로딩에 의한 설계보다 조금 더 복잡합니다.

근본적인 이유는 Instrument등의 인터페이스를 통한 간접참조(Indirection) 형태를 띄기

때문입니다. 하지만 이런 대가와는 비교할 수 없을 정도로 프로그램의 유연성과 각 클래스

간의 독립성은 증대된다는 것을 알 수 있습니다.

 

필자는 폴리포피즘을 이용하면 얻을 수 있는 이런 유연성 때문에 메소드의 인자와

객체의 레퍼런스에 인터페이스를 많이 사용합니다. 메소드의 인자와  = 문의 왼편에는

인터페이스를 많이 사용합니다.

예제 4 - 12 처럼 Violin을 객체를 만들 때, Vector객체를 만들 때 , Hashtable을 만들 때

레퍼런스는 인터페이스형으로 사용합니다.

 

 

             Instrument i = new Violin();

             List list = new Vector();

             Map map = new Hashtable();

예제 4 - 12

 

Posted by
,