혹자는 DIP(Dependency Inversion Principle)는 시스템의 유연성을 극대화 하기 위한 방법이라고 말하고 있습니다. 유연성을 극대화 하기 위해서 소스코드는 abstraction 에 의존해야하며 실제 구현체에는 의존 하지 않도록 시스템을 구성해야 합니다.
예를들어 Java의 경우 import구문은 interface나 abstraction class 선언만을 참조해야 한다는 말이죠.
위에서 말한것처럼 유연성을 극대화 하기 위한 방법중의 하나이기도 하며 제일 중요한 이유는 변동성이 큰 구현체에 대해서 의존성을 삭제 하는데에 있습니다.
예를 들어 A class가 B class를 import 후 사용하게 되면 A class는 B class를 의존하게 됩니다. 이때 B class를 수정하게 되면 이 영향은 A class도 받을 수 밖에 없습니다. 만약 A class는 B-1 class를 import해서 사용하고, B-1 class은 단지 abstraction class 혹은 interface이며 실제 구현체는 B class에 있을때에 B class를 수정하게 되더라도 abstraction 혹은 interface에 정의되어 있는 내용은 바뀌지 않기 때문에 A class는 다시 빌드 할 필요없이 그대로 사용할 수 있게 됩니다. 여기에서 B class는 B-1 class의 구현체이기 때문에 B-1에 정의되어 있는 기능은 꼭 구현을 해야만 합니다. 이말은 B class는 B-1 class를 의존하는 형태로 변경이 됩니다.
DIP를 위해서 여러 rule들이 있습니다. 이런 rule을 준수 하려면 변동성이 큰 구현체를 사용할때에 일반적인 방법으로 객체를 생성해서 사용하면 의존성이 발생하는것을 피할 수 없습니다.
따라서, 많은 객체 지향 언어(특히 Java)에서는 이런 의존성이 발생하는 것을 피하기 위해서 Factory pattern을 많이 사용합니다.
Clean Architecture: A Craftsman's Guide to Software Structure and Design (Robert C. Martin Series)
위의 그림에서 Application은 Service Interface를 이용해서 ConcreteImpl구현체를 사용합니다.
만약 일반적인 방법으로 Application에서 ConcreteImpl 구현체의 Instance를 생성하면 Application은 ConcreteImpl에 대한 의존성을 피할수 없습니다.
위의 그림과 같이 Application에서는 ServiceFactory interface에 정의 되어 있는 makeSvc를 호출하고 해당 interface의 구현체인 ServiceFactoryImpl에서 ConcreteImpl의 Instance를 생성한 후 Service type으로 반환을 하게 되면 Application에서는 ConcreteImpl의 구현체에 대한 Dependency가 사라지게 됩니다.
여기서 주요한 점은 system controlled flow는 그림의 아래쪽 방향, 붉은 라인의 위에서 아래쪽으로 진행 되지만 Dependency는 반대 방향인 붉은 라인의 아래쪽에서 위쪽으로 향하고 있습니다.
<aside> 👉 붉은 라인 아래쪽의 ServiceFactoryImpl과 ConcreteImpl간의 Dependency는 피할수 없다. 따라서, 대부분 system에서는 ServiceFactoryImpl과 같은 역활을 하는 instance를 main에서 생성후 전역으로 사용한다.
</aside>
이 예제에서는 단순히 interface만을 이용한 DIP(Dependency Inversion Principle)을 설명합니다. 이후에 위에 설명한 Factory Pattern을 이용한 예제도 함께 볼 예정입니다.
위의 그림과 같이 이 예제에서는 Socket라는 interface가 있고, Main Application에서는 두 종류의 Socket(LegacySocket, WebSocket)을 사용해서 서버와 interaction하는 부분을 보여줍니다.