Whiteship's Note


EJ2E Item 18. 추상 클래스 보다는 인터페이스를 선호하라

Java : 2009.01.19 01:43


참조: Effective Java 2nd Edition. Item 18: Prefer interfaces to abstract classes

기존 클래스를 쉽게 수정하여 새로운 인터페이스를 구현할 수 있다.
- 인터페이스는 implements에 추가해주고 필요한 매서드를 구현하면 끝
- 하지만 새로운 추상 클래스를 만들어서 공통 로직을 상위로 올린다면, 하면 하위 클래스에는 자신에게 적당할지 안 할지도 모를 로직들을 상속받게 된다.

믹스인을 정의할 때 인터페이스가 제격이다.
- 믹스인(Mixin)이란 "원래 타입"에 어떤 부가적인 행위를 추가로 구현했다는 것을 나타내는 타입. ex) Comparable 인터페이스
- 추상 클래스는 믹스인으로 쓰기 어렵다.(단일 상속이니까)

인터페이스 계층구조가 아닌 타입 프레임워크를 구성할 수 있게 한다.

인터페이스는 Item 16에서 살펴본 wrapper class 개념을 통해 안전하고, 강력한 기능성 증진을 가져다준다.
- 추상 클래스를 사용하여 타입을 정의하면, 개발자가 기능을 추가하고 싶을 때 상속만을 써야 한다. 그 결과 클래스가 wrapper 클래스보다 점점 약해지고 깨지기 쉬워진다.

인터페이스 장점과 추상 클래스 장점을 추상 skeletal 구현체 클래스를 제공하여 얻을 수 있다.
- skeletal 인터페이스 = Abstact인터페이스, 해당 인터페이스를 구현할 때 필요한 모든 작업을 포함하고 있다.
- 개발자가 해당 인터페이스를 쉽게 구현할 수 있도록 도와준다.
- 보통은 skeletal 인터페이스를 extends 하여 만들지만, 그렇게 못할 경우에는 인터페이스를 직접 implements해도 된다.
- 인터페이스를 구현하고 그 것을 구현할 때 skeletal 클래스를 상속 받은 private inner class에 매소드 호출을 위임하는 식으로 구현할 수 있다. (simulated multiple inheritance)

인터페이스를 배포 한 뒤 그 구현체가 많아졌다면 그것을 변경하는 건 불가능에 가깝다.
- 따라서 주의해서 설계해야한다.
- 인터페이스를 정하기 전 가능한 여러 개발자가 그것을 구현해보게 하는 것이 최선이다.

top


테스트 할 때 추상 클래스 활용하기

모하니?/Coding : 2007.09.23 19:24


테스트를 할 때 종종 같은 설정 파일을 필요로 하는 경우가 있습니다. 이럴 때 Abstract 클래스로 중복되는 설정파일을 읽어들이는 코드를 상위로 올리면 테스트 클래스를 작성할 때 매우 간결해 집니다.

public abstract class AbstractDaoTest extends AbstractTransactionalDataSourceSpringContextTests {

    @Override
    protected String[] getConfigLocations() {
        return new String[] { "file:web/WEB-INF/spring/hibernateContext.xml",
                "file:web/WEB-INF/spring/dataSourceContext.xml", "file:web/WEB-INF/spring/daoContext.xml", };
    }
}

이제 위 상위 클래스를 상속받아서 테스트 클래스를 작성하면 됩니다.
public class MemberDaoTest extends AbstractDaoTest{

    private MemberDao memberDao;

    public void setMemberDao(MemberDao memberDao) {
        this.memberDao = memberDao;
    }

    public void testAdd() throws Exception {
        Member member = new Member();
        memberDao.add(member);
        memberDao.flush();
        assertEquals(1, memberDao.getAll().size());
        assertNotNull(member.getMemberId());
    }

}

이것이 바로 말도 많고 탈도 많은 상속의 묘미가 아닐런지 생각해 봅니다.
top