Item21. Design Interfaces with Implementations in Mind
인터페이스는 구현하는 쪽을 생각해 설계하라
인터페이스는 자바 프로그래밍 언어에서 매우 중요한 역할을 한다. 특히, 인터페이스는 다양한 구현체 간의 계약을 정의하고, 코드의 유연성과 확장성을 높여준다. 그러나 인터페이스를 잘못 설계하면, 이후에 구현체를 작성하거나 유지보수할 때 여러 가지 문제를 일으킬 수 있다. 따라서 인터페이스를 설계할 때는 이를 구현할 개발자들을 고려하여 신중하게 설계해야 한다.
인터페이스 설계의 중요성
인터페이스는 일종의 계약(Contract)이다. 인터페이스가 잘 설계되면 다양한 구현체들이 인터페이스를 손쉽게 구현할 수 있으며, 확장성과 유지보수성이 높은 코드를 작성할 수 있다. 그러나 인터페이스를 잘못 설계하면 새로운 기능을 추가하거나 버그를 수정하는 과정에서 구현체들이 불필요하게 복잡해질 수 있다.
예를 들어, 인터페이스에 새로운 메서드를 추가하면 기존에 이 인터페이스를 구현한 모든 클래스에서 해당 메서드를 구현해야 한다. 이 경우, 기존의 구현체가 새로운 메서드와 관련된 기능을 필요로 하지 않더라도, 어쩔 수 없이 메서드를 구현해야 하므로 비효율적이고 복잡한 코드가 만들어질 수 있다.
Java 8의 디폴트 메서드와 인터페이스의 진화
자바 8에서는 인터페이스에 디폴트 메서드(Default Method)를 도입하여 인터페이스의 진화를 도모했다. 디폴트 메서드를 사용하면 인터페이스에 새로운 메서드를 추가하면서도 기존의 구현체에 영향을 주지 않을 수 있다. 그러나 디폴트 메서드의 도입이 무조건적인 해법은 아니며, 여전히 신중한 설계가 필요하다.
인터페이스 설계 시 고려해야 할 사항
인터페이스를 설계할 때는 다음과 같은 사항들을 고려해야 한다.
기본 구현 제공 여부: 가능한 경우 인터페이스에 기본 구현을 제공할 수 있는지 고려해야 한다. 기본 구현이 가능하다면, 디폴트 메서드로 제공하는 것이 좋다. 이를 통해 새로운 기능을 추가할 때 기존 구현체에 미치는 영향을 최소화할 수 있다.
불변성(Invariance): 인터페이스는 한 번 릴리스되면 이후에 쉽게 변경할 수 없다. 인터페이스에 새로운 메서드를 추가하거나, 기존 메서드의 시그니처를 변경하는 것은 호환성 문제를 야기할 수 있다. 따라서 인터페이스 설계 시 확장 가능성을 염두에 두고 신중하게 설계해야 한다.
구현의 어려움: 인터페이스를 설계할 때는 이를 구현하는 개발자가 얼마나 쉽게 구현할 수 있을지를 고려해야 한다. 지나치게 복잡한 메서드 시그니처나 다수의 메서드를 요구하는 인터페이스는 구현체를 작성하는 데 어려움을 줄 수 있다.
단일 책임 원칙: 인터페이스는 단일 책임 원칙(Single Responsibility Principle, SRP)을 따르는 것이 좋다. 즉, 인터페이스는 하나의 책임만을 가져야 하며, 이를 잘 정의된 작은 단위로 나누는 것이 중요하다. 이를 통해 인터페이스가 지나치게 복잡해지는 것을 방지할 수 있다.
디폴트 메서드의 단점과 주의사항
디폴트 메서드는 기존 구현체에 영향을 미치지 않고 새로운 기능을 추가할 수 있는 장점을 제공하지만, 몇 가지 단점과 주의사항이 있다.
다이아몬드 문제(Diamond Problem): 디폴트 메서드를 사용할 때, 다중 상속 구조에서 다이아몬드 문제가 발생할 수 있다. 이는 인터페이스들이 동일한 디폴트 메서드를 제공할 때 발생하며, 이 경우에는 구현 클래스에서 메서드를 오버라이드하여 해결해야 한다.
혼란을 유발할 수 있음: 디폴트 메서드는 인터페이스의 모든 구현체에서 동일하게 동작해야 한다. 만약 기본 구현이 모든 상황에 적합하지 않다면, 이를 오버라이드해야 할 수도 있다. 이 과정에서 오버라이드할 필요가 있는지 혼란스러울 수 있다.
인터페이스의 일관성: 인터페이스는 기본적으로 동작을 정의하는 것이 아니라, 동작을 명시하는 역할을 해야 한다. 디폴트 메서드는 이러한 인터페이스의 일관성을 무너뜨릴 수 있다. 따라서 디폴트 메서드를 추가할 때는 이 메서드가 정말로 모든 구현체에 적합한지 신중하게 고려해야 한다.
Last updated