classDiagram direction BT class User1 { } class User2 { } class User3 { } class OPS { op1() op2() op3() } OPS <-- User1 OPS <-- User2 OPS <-- User3
- 다수의 사용자가
OPS
클래스의 오퍼레이션을 사용함UserN
은opN
OPS
는 정적 타입 언어로 작성된 클래스
위와 같은 경우 User1
이 op2
, op3
를 전혀 사용하지 않음에도 이 두 메서드에 의존하게되고, 사용하지 않는 소스 코드가 변경되면 다시 컴파일한 후 새로 배포해야한다.
인터페이스 분리 원칙은 오퍼레이션을 인터페이스 단위로 분리하여 이러한 의존성을 제거한다.
classDiagram class User1 { } class User2 { } class User3 { } class U1Ops { << interface >> op1() } class U2Ops { << interface >> op2() } class U3Ops { << interface >> op3() } class OPS { op1() op2() op3() } OPS --|> U1Ops OPS --|> U2Ops OPS --|> U3Ops U1Ops <-- User1 U2Ops <-- User2 U3Ops <-- User3
User1
의 소스 코드는 U1Ops
와 op1
에는 의존하지만 OPS
에는 의존하지 않게 되어 OPS
에서 발생한 변경이 User1
과 전혀 관계없는 변경이라면, User1
을 다시 컴파일하고 새로 배포하는 상황은 초래되지 않는다.
ISP와 언어
정적 타입 언어는 사용자가 import
, use
, include
와 같은 타입 선언문을 사용하도록 강제한다.
이러한 ‘포함된(included)’ 선언문으로 인해 소스 코드 의존성이 발생하고, 이로 인해 재컴파일 도는 재배포가 강제되는 상황이 무조건 초래된다.
- 동적 타입 언어네서는 소스 코드에 이러한 선언문이 존재하지 않고 런타임에 추론이 발생한다.
- 소스 코드 의존성이 아예 없으며, 재컴파일과 재배포가 필요없다.
- 이러한 이유로 동적 타입 언어를 사용하면 정적 타입 언어를 사용할 때보다 유연하며 결합도가 낮은 시스템을 만들 수 있다.
ISP를 아키텍처가 아니라, 언어와 관련된 문제라고 결론내릴 여지가 있다.
ISP와 아키텍처
ISP를 사용하는 근본적인 동기를 살펴보면, 잠재되어 있는 더 깊은 우려사항을 볼 수 있다.
- 일반적으로 필요 이상으로 많을 걸 포함하는 모듈에 의존하는 것은 해로운 일이다.
- 소스 코드 의존성의 경우 불필요한 재컴파일과 재배포를 강제하기 때문이다.
위와 같은 문제는 물론 아키텍처 수준에서도 마찬가지의 상황이 발생한다.
flowchart LR a[System S] b[Framework F] c[Database D] a --> b --> c
S
시스템을 구축하며F
라는 프레임워크를 도입하려고한다.F
프레임워크는D
라는 특정 데이터베이스를 반드시 사용하도록 만들었다.S
는F
에 의존하고,F
는D
에 의존하게 된다.
위와 같은 상황에서 F
에서는 불필요한 기능, 즉 S
와는 전혀 관계없는 기능이 D
에 포함되어있다고 가정하면,
- 불필요한 기능으로 인해
D
내부가 변경되면,F
를 재배포 해야할 수도 있다. D
내부의 기능 중F
와S
에서 불필요한 기능에 문제가 발생해도F
와S
에 영향을 준다.
결론
불필요한 짐을 실은 무언가에 의존하면 예상치도 못한 문제에 빠진다.