기본적으로 인터페이스와 추상 클래스의 장단점에 대해서 짚고 넘어가보고자 한다.
장단점은 아래와 같다.
추상 클래스 vs 인터페이스
인터페이스
장점
- 1. 다중 상속 지원 : 여러 인터페이스를 구현할 수 있으므로 다중 상속을 지원한다.
- 2. 유연한 설계 : 인터페이스는 클래스와 상관없이 다른 클래스가 동일한 인터페이스를 구현하므로 클래스간 교체가 유연하다.
- 3. 표준화 : 인터페이스를 사용하여 다른 클래스 간에 표준 메서드 시그니처를 정의할 수 있으므로 코드의 일관성과 유지보수성을 높인다.
- 4. 믹스인에 안성 맞춤 : 믹스인은 해당 클래스가 특정 행위를 한다고 알려주는 방법으로 예시로는 Comparable이 있다.
단점
- 1. 구현 코드 부재 : 인터페이스는 메서드 시그니처만을 정의하고, 구체적인 구현 코드는 제공하지 않는다. 따라서 추가작업이 필요하다. 하지만 default 메서드 추가로 이를 해소
- 2. 변경 관리 어려움 : 기존 인터페이스를 변경하면 이를 구현하는 모든 클래스를 수정해야하므로 업데이트와 호환성 유지가 어려울 수 있다.
추상 클래스
장점
- 1. 일부 구현 제공 : 추상 클래스는 추상 메서드 외에도 구체적인 메서드를 포함할 수 있으므로, 공통 로직을 구현하거나 필수 메서드를 제공할 수 있다.
- 2. 코드 재사용 : 추상 클래스를 상속하여 여러 하위 클래스에서 공통 코드를 재사용 가능하다.
- 3. 선택적 구현 : 추상 클래스는 일부 메서드를 추상으로 만들고 하위 클래스에서 반드시 구현해야 할 메서드를 선택적으로 정의가 가능하다.
단점
- 1. 다중 상속 제한 : 특히, 자바는 단일 상속만 허용하며, 이미 다른 클래스를 상속한 경우 추상 클래스를 상속하는 것이 제한된다.
- 2. 유연성 부재 : 추상 클래스를 상속하면 이미 클래스 계층 구조를 형성하므로 유연성이 상대적으로 부족할 수 있다.
- 2. 메서드 오버라이딩 강제 : 추상 클래스의 추상 메서드(abstract로 선언)는 하위 클래스에서 반드시 오버라이딩되어야 한다. 이는 개발자에게 추가 작업을 강제하는 측면에서 단점으로 작용 가능
요약하자면, 인터페이스는 다중 상속, 클래스간 교체 유연, 클래스 간 표준화 제공을 해주지만 구현 코드가 없다는 점과 메서드 추가/삭제 시 해당 인터페이스를 구현하는 모든 클래스가 변경되어야하는 문제가 있다고 한다 하지만 default 메서드가 있기 때문에 단점 부분은 추상 클래스와 다르지 않은 것 같다.
추상 클래스는 메서드 내부를 구현할 수 있어 코드 재사용이 가능하고 추상 메서드와 일반 메서드가 있기 때문에 강제하는 부분을 설정할 수 있다는 장점이 있지만 다중 상속에 안되고 상속시 계층 구조가 형성되므로 유연성이 없다는 단점이 있다.
종합해보면, 장단점이 서로 보완이 잘되어 결과적으로 가장 큰 차이는 인터페이스는 다중 상속을 지원하지만 추상 클래스는 다중 상속을 제한하는 점이다. 이로 인해 저자는 인터페이스의 사용을 권하고 있다.
예를 들어, 사람 클래스를 인터페이스만으로 구현시 경찰 인터페이스, 남자 인터페이스 등 여러 인터페이스를 구현가능하지만, 이를 추상 클래스만으로 구현시 한 클래스 당 하나의 클래스만 상속 가능하므로 계층구조가 생기고 유연성이 떨어지는 것이다.
인터페이스와 추상 클래스의 장점 모두 취하기 : 템플릿 메서드 패턴
- 인터페이스와 추상 골격 구현 클래스를 통해 구현
- 인터페이스로는 타입을 정의하고 필요하면 디폴트 메서드 몇개를 함께 제공
- 골격 구현 클래스로 나머지 메서드 구현
- 위와 같이 설정시 단순히 골격 구현을 확장하는 것만으로 이 인터페이스를 구현하는데 필요한 일이 대부분 완성된다.
- 즉, 완벽히 동작하는 인터페이스 구현체를 제공할 수 있다.
- 보통 이름 지을 때 Interface - AbstractInterface 형태로 지음 예시 : List - AbstractList
AbstractList는 골격 구현으로 AbstractCollection 추상 클래스를 상속하고 List를 구현하고 있다.
골격 구현 내부에는 List의 메서드들을 작성해두었고 추가적인 메서드들이 있으며 AbstractCollection의 추상 메서드인 size 메서드와 AbstractList의 추상 메서드인 get 메서드를 익명함수로 던져서 생성한다면 완벽에 가까운 List를 구현할 수 있다.
-> 프로그래머의 일을 덜어준다.
-> 추상 클래스의 장점처럼 프로그래머가 구현을 하는 것을 도와준다.
-> 추상 클래스의 다중 상속 제약으로 부터 자유롭다.
만약, 프로그램 구조상 위와같이 골격 구현을 확장하지 못하는 처지라면?
1. 디폴트 메서드를 통해 위와 같은 장점을 누리자
2. 시뮬레이트한 다중 상속 사용
- 인터페이스를 구현한 클래스에서 해당 골격 구현을 확장한 private 내부 클래스를 정의하고 각 메서드 호출을 내부 클래스의 인스턴스에 전달한다.
2번 방법에서의 골격 구현 작성
1. 인터페이스를 잘 살펴, 기반이 되는 메서드를 선정한다. 이 기반 메서드들이 골격 구현에서 추상 메서드가 될 것이다.
2. 기반 메서드를 사용해 직접 구현 가능한 메서드들을 디폴트 메서드로 설정
3. Object의 메서드들은 디폴트 메서드로 제공해서는 안된다.
(만약, 모든 메서드가 기반 메서드거나 디폴트 메서드라면 골격 구현을 할 필요가 없다.)
4. 위를 제외하고 남은 메서드가 있다면 클래스를 만들어 남은 메서드들을 작성한다.
핵심 정리
일반적으로 다중 구현용 타입으로는 인터페이스가 적합하다. 복잡한 인터페이스라면 구현 수고를 덜어주는 골격 구현을 제공해보는 것을 고려하는 것이 좋다.
개인 견해
개인적인 생각이지만 그냥 인터페이스 쓰는게 좋은 것 같다. 저자가 말하는 추상 클래스의 장점은 강제를 통한 구현에 편의성을 부여하는 점이라고 느껴지는데, 개인적으로는 그냥 인터페이스의 디폴트 메서드와 일반 메서드 사용으로 충분히 강제/강제 안함이 가능하다.
코드
https://github.com/mokjaemin/EffectiveJAVA
'Language > Java Plus' 카테고리의 다른 글
Effective JAVA - Item22 : 인터페이스는 타입을 정의하는 용도로만 사용하라 (0) | 2023.10.10 |
---|---|
Effective JAVA - Item21 : 인터페이스는 구현하는 쪽을 생각해서 설계하라. (0) | 2023.10.09 |
Effective JAVA - Item19 : 상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라. (0) | 2023.10.08 |
Effective JAVA - Item18 : 상속보다는 컴포지션을 사용하라. (0) | 2023.10.07 |
Effective JAVA - Item17 : 변경 가능성을 최소화하라. (0) | 2023.10.05 |