JAVA 를 조금이라도 사용해본 프로그래머라면, interface 의 중요성을 알고 있을 것이라 생각합니다.


interface 위주의 개발은 작성자(인터페이스를 사용하여 기능 개발)의 입장에서는 인터페이스에 정의된 기능 외에는 고려하지 않아도 되며, 사용자(인터페이스로 작성된 기능 사용) 입장에서는 인터페이스가 원하는 시그니처만 충족해주면 모듈을 사용할 수 있습니다.


interface 는 모듈 개발 시 생각을 단순화시킬 수 있으며, 모듈 간의 유연성을 높일 수 있으므로 자주 애용하고 있으리라 믿어 의심치 않습니다. 

(블로그 주인장도 그러한가? ㅡㅡ^) 


라이브러리 설계자들은 이러한 interface 위주로 모듈을 설계하여 배포를 하며, 개발자들은 해당 라이브러리를 사용하여 보다 쾌적한 개발을 할 수 있습니다. 


그러나 이러한 interface 의 설계는 양날의 검과 같습니다. 

interface 의 변경 시, 이를 구현하고 있는 class 를 모두 수정해야하기 때문이죠. 

물론 interface 의 설계자와 개발자가 동일하여 해당 수정작업에 대해 커버를 할 수 있으면 다행이지만, 이미 배포를 받아 인터페이스를 해당 모듈을 사용하여 구현한 개발자들이 많다면 그것만큼 재앙도 없을 것입니다.


JAVA8 에서는 이를 위해 default, static 메소드가 등장하였습니다. 

이 개념을 사용하여 인터페이스 내부에 공통적으로 수행해야할 일을 정의할 수 있으며, 이를 통해 인터페이스의 변경 작업 시 호환성 문제를 어느정도 해결할 수 있습니다. 


아래는 디폴트 메소드를 사용한 예제입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
 * 비행이 가는하다는 것을 정의한 인터페이스.
 * 
 * @author Doohyun
 *
 */
public interface IFlyAble {
    /**
     * 이륙하는 방법에 대한 구현.
     */
    void fly();
    
    /**
     * 이륙할 수 있는 여부 구현.
     * 
     * @return
     */
    Boolean flyAbleYn();
    
    /**
     * 착지를 위한 기능은 fly able 일 때 가능..
     */
    default void landing() {
        if (flyAbleYn()) {
            System.out.println("착륙을 시도합니다.");
        }
    }
}
cs


처음 interface 설계에서는 [이륙을 할 수 있는가?(flyAbleYn)], [이륙(fly)] 에 대한 설계를 하여 구현하였지만, 이륙할 수 있는 클래스군 한정 착륙 기능을 수행할 수 있어야 했습니다.


이 기능은 JAVA8 에 새로 나온 개념인 디폴트 메소드로 해결을 하였으며, 위와 같이 [착륙(landing)] 기능이 구현된 것을 볼 수 있습니다.


JAVA8 이전 버전에서는 아마 이런식의 구현이 있었을 것입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
 * 비행이 가는하다는 것을 정의한 인터페이스.
 * 
 * @author Doohyun
 *
 */
public interface IFlyAble {
    /**
     * 이륙하는 방법에 대한 구현.
     */
    void fly();
    
    /**
     * 이륙할 수 있는 여부 구현.
     * 
     * @return
     */
    Boolean flyAbleYn();
}
 
/**
 * 비행이 가능한 객체에 대한 유틸 클래스
 * 
 * @author Doohyun
 *
 */
public class FlayAbleUtil {
    
    /**
     * 착지를 위한 기능은 fly able 일 때 가능..
     * 
     * <pre>
     *     default 메소드의 기능이 이러한 유틸로 제작되었을 것!
     * </pre>
     */
    public static void landing(IFlyAble flyable) {
        if (flyable.flyAbleYn()) {
            System.out.println("착륙을 시도합니다.");
        }
    }
}
cs


디폴트 메소드의 기능을 사용할 수 없으니, 공통적으로 사용가능성이 있는 모듈을 위와 같이 Util 클래스의 형태로 제작했을 것입니다.


이러한 패턴을 우리는 많이 본 적이 있습니다. 맞습니다. Collections!


1
2
3
4
5
6
7
8
9
10
11
List<Integer> numberList = IntStream.range(0100).boxed().collect(Collectors.toList());
 
/**
 * java8 이전의 정렬.
 */
Collections.sort(numberList, (a, b) -> a.compareTo(b));
 
/**
 * java8 에서 추가된 디폴트 메소
 */
numberList.sort((a, b) -> a.compareTo(b));
cs


JAVA8의 설계자는 위와 같이 유틸클래스에 있던 기능을 디폴트 메소드로 구현함으로써, 각 하위클래스들이 직관적으로 기능을 수행할 수 있도록 하였습니다. 


Collection interface 는 변경되었지만, 호환성에서는 아무런 문제가 없었습니다.


또한 JAVA8 부터 기본적으로 제공되는 함수형 인터페이스들도 람다조합 등의 기능을 동작하게 하기 위하여 default method 를 사용하고 있습니다.



default 메소드는 이외에도 선택적으로 구현해야하는 사항에 대한 stub 을 더이상 만들 필요가 없어졌습니다. 

예를들어 특정 인터페이스에서 일부 시그니처의 기능을 사용하는 때가 있고 아닐 때가 있다 가정하면 다음과 같이 선택적으로 시그니처를 구현할 수 있습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/**
 * 비행을 할 수 있는 인터페이스 명시.
 *
 * @author Doohyun
 *
 */
public interface IFlyable {
    /**
     * 날 수 있는 방법 명시.
     */
    void fly();
    
    /**
     * 음식으로 변환
     * @return
     */
    default Object toFoodObject() {
        throw new UnsupportedOperationException("해당 제품은 먹는 것을 지원할 수 없습니다.");
    }
}
 
/**
 * 닭 클래스 정의
 * 
 * @author Doohyun
 *
 */
public class Chicken implements IFlyable{
 
    @Override
    public void fly() {
        System.out.println("30 m 점");
    }
 
    @Override
    public Object toFoodObject() {
        System.out.println("닭을 튀기자!!!");
        ..  
        return obj;
    }
}
 
/**
 * 형 오리 정의
 * 
 * <pre>
 *     형 오리는 먹을 수 없음.
 * </pre>
 * 
 * @author Doohyun
 *
 */
public class BroDuck implements IFlyable{
    @Override
    public void fly() {
        System.out.println("10cm 점프");
    }
}
cs


디폴트 메소드는 위와 같이 인터페이스의 수정에 있어서 유연성을 줄 수 있는 도구입니다. 


더이상 interface 의 수정에 있어서 호환성에 대한 문제를 잠시 잊게 해주며, 어떤 면에서보면 단순히 틀이 아닌 모듈로써의 역할까지 수행할 수 있어 보입니다.


그러나 편의적인 면과 더불에 작은 문제가 생겼습니다. 

각 interface 들간 깊은 상속관계가 구축되어 있으며, 모듈화된 디폴트 메소드에 의해 다중상속 문제가 등장하였습니다.


이에 대한 풀이를 다음 포스팅에서 하고자 합니다.




자바 8 인 액션
국내도서
저자 : 라울-게이브리얼 우르마(RAOUL-GABRIEL URMA),마리오 푸스코(MARIO FUSCO),앨런 마이크로프트(ALAN MYCROFT) / 우정은역
출판 : 한빛미디어 2015.04.01
상세보기


반응형
Posted by N'