[교재 EffectiveJava] 아이템 20. 추상 클래스보다는 인터페이스를 우선하라

반응형
728x90
반응형

다중 구현 메커니즘

자바가 제공하는 다중 구현 메커니즘은 인터페이스, 추상클래스 2가지다. 자바 8부터 인터페이스도 디폴트 메서드를 제공할 수 있게되었다.

 

디폴트 메서드란?

https://devfunny.tistory.com/350

 

자바8의 default 메서드 등장

디폴트 메서드의 등장 자바 8에서는 기본 구현을 포함하는 인터페이스를 정의하는 2가지 방법을 제공한다. 만약 인터페이스를 바꾸게 되었을때, 해당 인터페이스를 구현한 모든 클래스의 구현

devfunny.tistory.com

 

 

추상클래스

추상 클래스와 인터페이스의 가장 큰 차이는 추상 클래스가 정의한 타입을 구현하는 클래스는 반드시 추상 클래스의 하위 클래스가 되어야한다는 점이다. 자바는 단일 상속만 가능하므로, 추상 클래스 방식은 새로운 타입을 정의하는데 큰 제약을 갖게된다. 두 클래스가 같은 추상 클래스를 상속한다면 그 추상 클래스는 계층 구조 상 두 클래스의 공통 조상이어야 한다. 이 방식은 클래스 계층 구조에 커다란 혼란을 일으킨다. 새로 추가된 추상 클래스의 모든 자손이 이를 상속하게 되어 강제성이 생겨버린다.

 

 

인터페이스

인터페이스는 선언한 메서드를 모두 정의하고 그 일반 규약을 잘 지킨 클래스라면 다른 어떤 클래스를 상속했든 같은 타입으로 취급한다. 기존 클래스에서 손쉽게 새로운 인터페이스를 구현해넣을 수 있다. (public class implents interface)  인터페이스로는 계층 구조가 없는 타입 프레임워크를 만들 수 있다. 하나의 클래스에서 2개 이상의 인터페이스 모두를 구현해도 전혀 문제가 없다. 

 

인터페이스의 메서드 중 구현 방법이 명백한 것이 있다면, 그 구현을 디폴트 메서드로 제공하여 프로그래머들의 일감을 덜어줄 수 있다. 

 

디폴트 메서드의 사용 예제

https://devfunny.tistory.com/413

 

[Java8] 인터페이스 default 메소드 사용 예제와 인터페이스 메서드 추가 유연하게 구현방법

상황분석 우리에게 운영중인 레거시 프로젝트가 있다고 가정해보자. 그리고 UserDto 파일이 존재하는데, 해당 Dto 파일은 User 테이블의 필드들을 담고있는 클래스이다. UserDto.java public class UserDto imp

devfunny.tistory.com

디폴트 메서드를 제공할때는 상속하려는 사람을 위한 설명을 @implSpec 자바독 태그를 붙여 문서화 해야 한다. 디폴트 메서드에도 제약은 있다. 인스턴스 필드를 가질 수 없고, public 이 아닌 정적 멤버도 가질 수 없다. (private 정적 메서드는 예외) 

 

 

템플릿 메서드 패턴

템플릿 메서드 패턴의 자세한 내용은 아래 포스팅을 참고하자.

https://devfunny.tistory.com/899

 

템플릿 메서드 패턴 (Template Method Pattern)

수강완료한 강의 복습해보자 (코딩으로 학습하는 GoF의 디자인 패턴) 템플릿 메서드 (Factory method) 알고리즘 구조를 서브 클래스가 확장할 수 있도록 템플릿으로 제공하는 방법. 추상 클래스는 템

devfunny.tistory.com

 

인터페이스와 추상 골격 구현 클래스를 함께 제공하는 방식으로 인터페이스와 추상 클래스의 장점을 모두 취할수도 있다. 인터페이스로는 타입을 정의하고, 필요하면 디폴트 메서드 몇개도 함께 제공한다. 그리고 골격 구현 클래스는 나머지 메서드들까지 구현한다. 이렇게 단순히 골격 구현만 확장하는 것만으로 이 인터페이스를 구현하는데 필요한 일이 대부분 완료된다. 

- 인터페이스 이름 : Interface
- 골격 구현 클래스 이름 : AbstractInterface (예시: 컬렉션 프레임워크의 AbstractCollection, AbstractSet 등)

 

예시. AbstractList.java
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
	...
    
    public abstract E get(int index);
    
    public E set(int index, E element) {
        throw new UnsupportedOperationException();
    }
    
    public abstract int size();
}

 

 

골격 구현 작성 방법

1) 인터페이스를 잘 살펴 다른 메서드들의 구현에 사용되는 기반 메서드들을 선정한다.

public interface Test {
    public void get(int value);
    public void set(int value);
}

 

2) 기반 메서드들은 골격 구현에서는 추상 메서드가 된다.

3) 기반 메서드들을 사용해 직접 구현할 수 있는 메서드를 모두 디폴트 메서드로 제공한다.

interfaceTest.java
public interface Test {
    public void get(int value);
    public void set(int value);
    
    default void remove(int value) {
        System.out.println("remove : " + value);
    }
}

 

4) equals, hashCode와 같은 Object의 메서드는 디폴트 메서드로 제공하면 안된다.

5) 인터페이스의 메서드가 모두가 기반 메서드와 디폴트 메서드가 된다면 골격 구현 클래스를 별도로 만들 이유는 없다.

6) 기반 메서드나 디폴트 메서드로 만들지 못한 메서드가 남아있다면, 이 인터페이스를 구현하는 골격 구현 클래스를 하나 만들어 남은 메서드들을 작성해 넣는다.

 

abstract class AbstractTest.java
/**
 * 추상 골격 구현 클래스
 */
public abstract class AbstractTest implements Test {
    @Override
    public void get(int value) {
        System.out.println("get : " + value);
    }
}

 

class TestSub.java
public class TestSub extends AbstractTest implements Test {
    @Override
    public void set(int value) {
        System.out.println("set : " + value);
    }
}

 

class TestSub2.java
public class TestSub2 extends AbstractTest implements Test {
    @Override
    public void set(int value) {
        System.out.println("set2 : " + value);
    }
}

 

6) 골격 구현 클래스에는 필요하면 public 이 아닌 필드와 메서드를 추가해도 된다.

7) 결과

위 메서드를 실행하는 Main Class
public class Main {
    public static void main(String[] args) {
        TestSub testSub = new TestSub();
        testSub.get(1); // get : 1
        testSub.set(2); // set : 2
        testSub.remove(2); // remove : 2

        TestSub2 testSub2 = new TestSub2();
        testSub2.get(1); // get : 1
        testSub2.set(2); // set2 : 2
        testSub2.remove(2); // remove : 2
    }
}

 

결국 TestSub.java 와 TestSub2.java 의 get 메서드는 공통 로직이기 때문에 AbstractTest 에서 구현되었고, 이를 상속한 각 TetsSub.java, TestSub2.java 는 아직 구현하지 않은 set 메서드를 각 클래스 별로 재정의했다.

 

 

 

 

 

반응형

Designed by JB FACTORY