3 분 소요

Reference

스프링 입문을 위한 자바 객체 지향의 원리와 이해, 위키북스, 김종민 지음

개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴, 인투북스, 최범균 지음


📌 인터페이스 변경과 그 영향

C++로 게시판 모듈을 설계할 때 게시글 목록, 작성, 삭제 기능을 제공하는 ArticleService 클래스를 구현할 경우, 인터페이스를 분리하지 않고 헤더 파일인 ArticleService.h 에 모든 기능이 명세가 되어 있도록 설계해 보자.

이때 게시글 목록과 관련된 기능에 수정이 필요한 상황이 발생한다고 가정한다. 이때 역할에 따른 인터페이스 분리를 하지않은 문제점이 발생하게 된다.

  • 헤더 파일인 ArticleService.hArticleService.cpp 에 수정을 반영하고 컴파일을 통해 오브젝트 파일을 생성한다.
    • ArticleService.h 가 목록, 작성, 삭제에 대한 모든 기능을 제공하고 있다.
  • 헤더 파일이 변경되어 이 헤더 파일을 사용하는 모든 소스 코드도 다시 컴파일해서 오브젝트 파일을 만들어 주어야 한다. 즉, 목록과 관련되지 않은 작성, 삭제 기능을 다시 컴파일해야 하는 불편함이 발생한다.

인터페이스 분리가 되어 있지 않으면 불필요한 곳에 변경의 여파가 미친다.


📌 ISP: 역할의 분리

클라이언트는 자신이 사용하는 메서드에만 의존해야 한다.

  • 인터페이스는 그 인터페이스를 사용하는 클라이언트를 기준으로 분리해야 한다.

클라이언트별 요구사항을 세분화하면 위 ArticleService.h를 총 세가지로 나눌 수 있다.

  • 목록 클라이언트: ArticleListService.h
  • 작성 클라이언트: ArticleWriteService.h
  • 삭제 클라이언트: ArticleDeleteService.h

역할별로 분리하면 목록 UI는 목록에 관련된 헤더 파일이 변경되었을 때 재컴파일이 필요하고, 각각의 UI들도 관련된 헤더 파일이 변경되었을 때만 재컴파일하면 된다.

재컴파일의 문제?

C++ 언어의 경우 직접 링크 과정을 하기 때문에 인터페이스 분리 원칙을 따르지 않으면 사용하지 않는 인터페이스 변경에 의해 발생하는 소스 재컴파일 문제가 발생하게 된다.

하지만, 자바 언어는 컴파일을 통해 .class 파일을 생성 하면되고, 링크 과정은 JVM이 클래스 파일을 로딩하며 동적으로 발생하기 때문에 재컴파일 문제가 발생하지 않는다.

단순히 재컴파일의 문제이면 자바는 인터페이스 분리 원칙이 빠진 SOLD 원칙만 준수하면 된다.

  • 인터페이스 분리 원칙은 소스 재컴파일 문제만 관련된 것이 아니다.
  • 인터페이스를 분리하는 기준은 역할이며, 이는 단일 책임 원칙(SRP)과도 연결된다.

하나의 클래스 파일에 여러 기능이 섞여 있으면 한 기능의 변화로 인해 다른 기능이 영향을 받을 가능성이 높아진다. 따라서 클라이언트 입장에서 사용하는 기능만 제공하도록 인터페이스를 분리해 한 기능에 대한 변경의 여파를 최소화할 수 있게 된다.

  • 클라이언트가 필요로 하는 기능을 세분화해 각각 하나의 역할(인터페이스)로 정의한다.
    • 인터페이스 분리는 인터페이스와 구현 클래스의 재사용성을 높여 주는 효과를 갖는다.


📌 ISP: 클라이언트 지향

인터페이스 분리 원칙은 클라이언트 입장에서 인터페이스를 분리하라는 원칙이다. 특정 클라이언트를 위한 인터페이스 여러 개가 범용(한 개의) 인터페이스보다 낫다.

  • A가 B를 의존할 경우 B의 변화로 인해 A가 변경되지만, 반대로 A의 요구에 의해 B가 변경될 수도 있다.
    • ArticleService.h의 변화로 목록 UI에 영향을 미치지만, 반대로 목록 UI의 변화 요구에 의해 ArticleService.h가 변경될 수 있다.
  • 이는 인터페이스를 분리하는 기준이 클라이언트가 된다는 것을 의미한다
    • 클라이언트 별로 인터페이스를 분리해 클라이언트로부터 발생하는 인터페이스의 변경 여파가 다른 클라이언트에 미치는 영향을 최소화할 수 있게 된다.


📌 ISP vs. SRP

  • 단일 책임 원칙(SRP)과 인터페이스 분할 원칙(ISP)은 같은 문제에 대한 두 가지 다른 해결책이라고 볼 수 있다.
    • 프로젝트 요구사항과 설계자의 취향에 따라 두 원칙 중 하나를 선택해 설계할 수 있다.
    • 하지만 특별한 경우가 아니라면 SRP를 적용하는 것이 더 좋은 해결책이라고 할 수 있다.

인터페이스 분할 원칙(ISP)을 이야기할 때 항상 함께 등장하는 것이 인터페이스 최소주의 원칙이다. 인터페이스를 통해 메서드를 외부에 제공할 때는 최소한의 메서드만 제공하라는 것이다.

  • 상위 클래스는 풍성할수록 좋고, 인터페이스는 작을수록 좋다.

풍성한 상위 클래스의 장점

리스코프 치환 원칙(LSP)에 따라 하위 객체는 상위 객체인척 할 수 있다.

  • 빈약한 상위 클래스
    • 상위 객체에 하위 타입의 객체를 참조할 때, 메서드 사용 시 여기저기 형변환이 발생해 상속의 장점을 활용하지 못할 수 있다.
    • 하위 타입의 메서드를 사용하지 못한다.
class Person {

	private String name;

	public void eat() {
		System.out.println("음식 먹기");
	}
}

class Student extends Person {

	private String birthDay;
	private String identificationNumber;
	private String studentId;

	public void sleep() {
		System.out.println("자기");
	}

	public void study() {
		System.out.println("공부하기");
	}
  
	public void presentation() {
		System.out.println("학생 입니다.");
	}

}

class Soldier extends  Person {

	private String birthDay;
	private String identificationNumber;
	private String soliderId;

	public void sleep() {
		System.out.println("자기");
	}

	public void train() {
		System.out.println("훈련하기");
	}
  
	public void presentation() {
		System.out.println("군인 입니다.");
	}
	
}
  • 풍성한 상위 클래스
    • 객체와 메서드를 사용 불가능한 경우가 없고, 불필요한 형변환이 없다.
    • 추상 메서드 오버라이딩을 통해 기능을 확장할 수 있다.
abstract class Person {

	private String name;
	private String birthDay;
	private String identificationNumber;

	public void eat() {
		System.out.println("음식 먹기");
	}

	public void sleep() {
		System.out.println("자기");
	}

	public abstract void presentation();

}

class Student extends Person {

	private String studentId;

	public void study() {
		System.out.println("공부하기");
	}

	@Override
	public void presentation() {
		System.out.println("학생 입니다.");
	}
}

class Soldier extends Person {

	private String soliderId;

	public void train() {
		System.out.println("훈련하기");
	}

	@Override
	public void presentation() {
		System.out.println("군인 입니다.");
	}
}

인터페이스가 작을수록 좋은 이유

인터페이스는 “~할 수 있는(is able to)”이라는 기준으로 만드는 것이 정석이다.

  • 인터페이스는 그 역할에 충실한 최소한의 기능만 공개하라는 것이 객체 지향 대가들의 가르침이다.
  • 인터페이스는 클라이언트의 관심 영역 내의 최소 기능만 제공해야 한다.

댓글남기기