Runtime에 유일한 상태를 공유하기 위해서 singleton을 많이 사용하는데, 우리가 singleton class를 만들때 기대하는 바는 위 [그림 1]과 같다. Singleton은 다른 object들과의 dependency 관계가 쉽게 맺어지고 사용될 수 있도록 구현되어 있다. 이게 singleton의 장점이자 단점으로 다가오는데, 시간이 지나면서 개발자들은 singleton이 존재해야 할 레이어에 대한 인지가 점점 떨어진다. 특히 급하게 처리되어야 할 문제들이 쉽게 해결할 수 있는 singleton으로 모이게 되는데, 이런식으로 처음 의도와는 다르게 여러 service들이 하나의 singleton을 의존하게 된다. 그리고 여러 service들에 의해 많은 기능들이 추가 되면서 점점 large class가 되어 간다. 심지어 callback을 주기위해 역참조하는 경우도 발생한다.
[그림 2]처럼 service A를 위해 만들어졌던 func1이 다른 service들이 사용하기 시작한다. service A의 요구 사항이 변경되어 func1이 변경된다면 이를 사용하는 모든 service들에게 변경 전파(Shotgun Surgery)가 이루어진다. 그리고 이러한 coupling 문제를 개선하기 위해 singleton을 리팩토링 할 때의 영향도(Force)는 어플리케이션 전체가 된다.
Singleton은 class dependency라서 mock으로 대체 될 수 없다. 이는 singleton을 사용하는 service들의 unit test를 작성하는데 어렵게 만든다. singleton의 변경에 따른 영향도는 굉장히 높지만 그 변경에 대한 검증이 매우 부족한 상태라는 의미다.
Singleton에 대한 class dependency 문제를 해결하기 위해서는 의존 관계가 외부로부터 injection 되어야 한다. 그리고 client는 이렇게 주입받은 object가 singleton instance인지 일반 object intacne인지 모르는 상태에서 사용되어야 한다. 또한 이 object는 interface에 의존하도록 디자인 되어야 한다. 이는 통제된 인터페이스를 통해 정의된 서비스를 제공하도록 디자인됨을 의미한다. 그리고 해당 object의 유일성은 Service Locator같은 녀석이 책임지고 dependency wiring은 IoC Container로부터 injection 받도록 디자인 해볼 수 있다.
'Architecture & Design Pattern' 카테고리의 다른 글
제어의 역전(Inversion of Control, IoC) 이란? (16) | 2019.12.08 |
---|---|
if문을 없애는 디자인 (0) | 2019.11.17 |
Data Access Object Pattern (0) | 2019.09.07 |
iOS Clean Architecture with Swift (1) | 2019.07.13 |