[OOP] 인터페이스 분리 원칙(ISP: Interface Segregation Principle) 개념 및 예제
객체 지향 설계 원칙(SOLID)
객체 지향 언어의 등장 이후 수많은 시행착오와 베스트 프랙티스 속에서 객체 지향 설계 5가지 원칙이 등장했는데, 바로 SOLID다. SOLID는 로버트 C. 마틴(Robert C. Martin)이 2000년대 초반 객체 지향 프로그래밍 및 설계의 다섯 가지 기본 원칙으로 제시한 것을 마이클 페더스(Michael Feathers)가 두문자어로 소개한 것이다.
SOLID는 다음 5가지 원칙의 앞 글자를 따서 부르는 이름이다.
- SRP(Single Responsiblity Principle) : 단일 책임 원칙
- OCP(Open Closed Principle) : 개방 폐쇄 원칙
- LSP(Liskov Substitution Principle) : 리스코프 치환 원칙
- ISP(Interface Segregation Principle) : 인터페이스 분리 원칙
- DIP(Dependency Inversion Principle) : 의존 역전 원칙
이 원칙들은 응집도는 높이고(High Cohesion), 결합도는 낮추라(Loose Coupling)는 고전 원칙을 객체지향의 관점에서 재정립한 것이다.
인터페이스 분리 원칙(ISP: Interface Segregation Principle)
“클라이언트는 자신이 사용하지 않는 메서드에 의존 관계를 맺으면 안 된다.”
- 로버트 C. 마틴 -
인터페이스 분리 원칙(ISP)은 큰 인터페이스를 작은 단위로 분리하여 클라이언트의 목적에 알맞은 기능을 제공하는 것이다. ISP의 간단한 예를 살펴보자.
ISP를 위반하는 경우
운송 수단을 의미하는 Vehicle 인터페이스와 이를 구현하는 Car(자동차), 비행기(Airplane) 클래스가 있다고 가정하자.
interface Vehicle {
void go();
void fly();
}
class Car implements Vehicle {
@Override
public void go() {
// ...
}
@Override
public void fly() {
// ...
}
}
class Airplane implements Vehicle {
@Override
public void go() {
// ...
}
@Override
public void fly() {
// ...
}
}
- Vehicle이라는 추상 클래스 또는 인터페이스에는 go()와 fly()라는 추상 메서드가 존재한다.
- 만약, Car 클래스와 Airplane 클래스가 Vehicle의 구현체로 구현한다면, Car는 날지 못함에도 불구하고 fly() 메서드를 구현하게 된다.
- Car 클래스의 목적에 맞지 않는 fly() 메서드로 인해 인터페이스 분리 원칙을 위반한다.
따라서 Vehicle을 다음과 같이 두 인터페이스로 분리한다.
interface Move {
void go();
}
interface Fly extends Move {
void fly();
}
class Car implements Move {
@Override
public void go() {
// ...
}
}
class Airplane implements Fly {
@Override
public void go() {
// ...
}
@Override
public void fly() {
// ...
}
}
- Vehicle을 Move와 Fly 인터페이스로 분리하여 go() 메서드와 fly() 메서드를 별도로 구현하도록 한다.
- Car 클래스는 Move 인터페이스를 구현하여 go() 메서드만 사용할 수 있도록 한다.
- Fly 인터페이스는 Move 인터페이스를 상속하고, Airplane 클래스는 Fly 인터페이스를 구현하여 go()와 fly() 모두를 구현할 수 있도록 한다.
- 위와 같이 구성할 경우 Car와 Airplane 클래스는 목적에 알맞은 기능만을 구현하게 된다.
단일 책임 원칙과 인터페이스 분리 원칙
이전 게시글에서 정리한 단일 책임 원칙(SRP) 예제를 통해 살펴보자.
https://ittrue.tistory.com/534
다양한 책임을 가진 남자 클래스
위 다이어그램의 남자 클래스에 단일 책임 원칙을 적용하면 다음과 같이 단일 책임을 가진 클래스로 나뉜다.
- 단일 책임 원칙은 여러 책임을 가진 클래스를 분리하여 하나의 책임만을 가지도록 하는 원칙이다.
위 예제를 단일 책임 원칙이 아닌 인터페이스 분리 원칙을 적용시킬 수 있다.
- 단일 책임 원칙을 적용시키는 것이 아닌 남자 클래스를 인터페이스로 제한하는 방법이다.
- 여자친구를 만날 때는 남자친구 역할만 수행하고, 직장 상사 앞에서는 사원 역할만 수행하도록 제한한다.
단일 책임 원칙(SRP)과 인터페이스 분리 원칙(ISP)은 같은 문제에 대한 두 가지 다른 해결책으로 볼 수 있다. 실제 프로젝트를 요구사항과 설계자 취향에 따라 단일 책임 원칙이나 인터페이스 분리 원칙 중 하나를 선택하여 설계할 수 있다.
이 두 원칙은 비슷한 개념을 공유하지만 객체 지향 설계에서 상호 보완적으로 사용될 수 있다.
- SRP는 각 클래스가 자체적으로 변경되어야 하는 이유가 하나여야 한다.
- ISP는 인터페이스를 분리하여 클라이언트가 필요로 하는 기능만 사용하도록 한다.
인터페이스 최소주의 원칙
ISP를 이야기할 때 함께 등장하는 원칙 중 하나로 인터페이스 최소주의 원칙이 있다. 이것은 인터페이스를 통해 메서드를 외부에 제공할 때는 최소한의 메서드만 제공하라는 것이다.
- 리스코프 치환 원칙(LSP)에 따라 하위 객체는 상위 객체인 척할 수 있다.
- 따라서 상위 클래스는 풍성할수록 좋고, 인터페이스는 작을수록 좋다.
빈약한 상위 클래스 vs 풍성한 상위 클래스
- 빈약한 상위 클래스인 경우 하위 클래스인 학생 클래스와 군인 클래스는 같은 속성과 메서드(생일, 주민등록번호, 자다(), 소개하다())를 공통적으로 가지고 있는 것을 볼 수 있다.
- 풍성한 상위 클래스인 경우 상위 클래스가 하위 클래스들이 공통으로 가질 수 있는 속성과 메서드를 상속해주고 있다.
빈약한 상위 클래스를 이용하는 경우
- 불필요한 형변환이 발생하면서 상속의 혜택을 제대로 누리지 못한다.
- 객체의 참조 변수를 학생이나 군인으로 선언하면 되겠지만, 그럼 굳이 상속 구조를 만들 필요도 없어진다.
- 상위 클래스형의 참조 변수를 이용해야 상속의 가장 큰 혜택을 볼 수 있다.
풍성한 상위 클래스를 이용하는 경우
- 사용 불가능한 경우나 불필요한 형변환이 없다.
- 소개하다()와 같은 메서드는 학생과 군인의 소개 내용이 같다는 것은 이치에 맞지 않지만, 둘 다 필요하다. 이 경우 추상 메서드를 사용할 수 있다.
참고 서적
https://link.coupang.com/a/baQPj6
"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."