이번 장에서는 의존성을 위주로 이야기를 풀어나갔다 생각합니다. 특히 저는 Spring을 공부하면 의존성에 대해 어깨너머로 배우고 또 느낀것이 있었기에 유독 흥미가 갔던 장이였던것 같습니다.
객체지향 설계의 핵심은 협력을 위해 필요한 의존성은 유지하면서도 변경을 방해하는 의존성은 제거하는데 있다.
01 의존성 이해하기
변경과 의존성
실행시점: 의존하는 객체가 정상적으로 동작하기 위해서는 실행 시에 의존 대상 객체가 반드시 존재해야 한다.
구현 시점: 의존 대상 객체가 변경될 경우 의존하는 객체도 함꼐 변경된다.
...
어떤 객체가 예정된 작업을 정상적으로 수행하기 위해 다른 객체를 필요로 하는 경우 두 객체 사이에 의존성이 존재한다고 말한다. 의존성은 방향성을 가지며 항상 단방향이다.
...
의존성은 변경에 의한 영향의 전파 가능성을 암시한다.
의존성에 대한 사전적인 정의에 대해 이야기를 하였으며 나아가 실제 설계에서는 어떤 의미를 가지는지 글이라 생각합니다. 해당 대목을 읽기 이전까진 의존성이란 그저 다른 객체에 대한 정보를 알고 있는 것으로만 알고 있었습니다. 그러나 "변경"에 대한 관점으로 생각의 전환을 할 수 있었습니다
의존성 전이
의존성 전이가 의미하는 것은 PeriodCondition이 Screening에 의존할 경우 PeriodCondition은 Screening이 의존하는 대상에 대해서도 자동적으로 의존하게 된다는 것이다.
...
의존성은 함께 변경될 수 있는 가능성을 의미하기 때문에 모든 경우에 의존성이 전이되는 것은 아니다.
...
의존성은 전이될 수 있기 때문에 의존성의 종류를 직접 의존성과 간접 의존성으로 나누기도 한다.
...
PeriodCondition이 Screening에 의존하는 경우가 여기에 속하며 이 경우 의존성은 PeriodCondition의 코드에 명시적으로 드러난다.
...
직접 의존성이란 말 그대로 한 요소가 다른 요소에 직접 의존하는 경우를 가리킨다.
...
간접 의존성이란 직접적인 관계는 존재하지 않지만 의존성 전이에 의해 영향이 전파되는 경우를 가리킨다.
의존성에 대해 깊게 파고들며 이야기를 하고 있습니다. 또한 의존성은 전이가 될 수 있다는 것이 해당 대목의 요지라 생각하며 코드상에서 직접 의존을 하며 로직을 풀어나가는 직접 의존성(저는 그 동안 직접 의존성과 간접 의존성을 구분하지 않았습니다.) 그리고 간접적으로 영향을 미치는 간접 의존성을 나타냅니다.
런타임 의존성과 컴파일타임 의존성
런타임 의존성이 다루는 주제는 객체 사이의 의존성이다. 반면 코드 관점에서 주인공은 클래스다. 따라서 컴파일 타임 의존성이 다루는 주제는 클래스 사이의 의존성이다.
...
사실 유연하고 재사용 가능한 코드를 설계하기 위해서는 두 종류의 의존성을 서로 다르게 만들어야 한다.
책에서 이야기하는 런타임 의존성은 말 그대로 런타임시, 실행될때 결정되는 의존성을 이야기합니다. SOLID의 DIP로 예를 들어 설명하면 런타인 의존성은 의존성 주입이 외부에서 이루어졌는것을 의미하며 컴파일 타임 의존성은 의존하는 객체간 메시지를 송수신이 가능하다는 것에 초점을 맞추고 있는듯 합니다.
컨텍스트 독립성
클래스가 사용될 특정한 문맥에 대해 최소한의 가정만으로 이뤄져 있다면 다른 문맥에서 재사용하기가 더 수월해 진다. 이를 컨텍스트 독립성이라고 부른다.
위에서 하던 이야기의 연장선으로 컨텍스트란 객체간 의존하고 있는 상황 그 자체를 이야기하는 것이며 최소한의 가정은 추상화를 의미합니다.
의존성 해결하기
컴파일 타임 의존성을 실행 컨텍스트에 맞는 적절한 런타임 의존성으로 교체하는 것을 의존성 해결이라고 한다.
- 객체를 생성하는 시점에 생성자를 통해 의존성 해결
- 객체 생성후 setter메서드를 통해 의존성 해결
- 메서드 실행시 인자를 이용해 의존성 해결
의존성을 해결하기 위한 방법으로 3가지를 제시하고 있습니다. 여기서 저는 개인적으로 선호하는 방식은 생성자를 통해 의존성을 해결하는 방법이며 이는 제가 사용하는 Spring Framework에서도 동일하게 사용하고 있습니다.그리고 setter를 통한 의존성 해결 방법은 객체의 상태를 외부에서 제어할 수 있게끔 만든다고 생각하기에 꺼리는 방법입니다. 마지막으로 메서드 실행시 인자를 이용한 의존성 해결방법 역시 자제하는 편입니다.
02 유연한 설계
의존성과 결합도
모든 의존성이 나쁜 것은 아니다. 의존성은 객체들의 협력을 가능하게 만드는 매개체라는 관점에서는 바람직한 것이다. 하지만 의존성이 과하면 문제가 될 수 있다.
...
문제는 의존성의 존재가 아니라 의존성의 정도다.
...
다시 말해 컨텍스트에 독립적인 의존성은 바람직한 의존성이고 특정한 컨텍스트에 하게 결합된 의존성은 바람직하기 않은 의존성이다.
...
결합도가 바로 그것이다. 어떤 두 요소 사이에 존재하는 의존성이 바람직할 때 두 요소가 느슨한 결합도 또는 약한 결합도를 가진다고 말한다. 반대로 두 요소 사이의 의존성이 바람직하지 못할 때 단단한 결합도 또는 강한 결합도를 가진다고 말한다.
책의 주된 내용이기도 한 의존성과 결합도의 정의를 말하고 있습니다. 또한 의존성은 무조건 나쁜 것은 아니며 의존하는 정도에 따라 바람직한 의존성과 그렇지 않은 의존성으로 나뉜다고 이야기하고 있습니다.
지식이 결합을 낳는다.
결합도의 정도는 한 요소가 자신이 의존하고 있는 다른 요소에 대해 알고 있는 정보의 양으로 결정된다.
...
더 많이 알수록 더 많이 결합된다. 더 많이 알고 있다는 것은 더 적은 컨텍스트에서 재사용 가능하다는 것을 의미한다.
이전 장에서 다룬 추상화를 이용해 결합도를 관리하라고 책에서 이야기하고 있습니다.
추상화에 의존하라
추상화를 사용하면 현재 다루고 있는 문제를 해결하는 데 불필요한 정보를 감출 수 있다. 따라서 대상에 대해 알아야 하는 지식의 양을 줄일 수 있기 때문에 결합도를 느슨하게 유지할수 있다.
...
다시 말해 의존하는 대상이 더 추상적일수록 결합도는 더 낮아진다는 것이다. 이것이 핵심이다.
책에서는 구체 클래스를 의존할 바에는 추상 클래스. 추상 클래스에 의존할 바에는 인터페이스를 의존하라고 이야기해주고 있습니다.
명시적인 의존성
결합도를 느슨하게 만들기 위해서는 인스턴스 변수의 타입을 추상 클래스나 인터페이스로 선언하는 것만으로는 부족하다. 클래스 안에서 구체클래스에 대한 모든 의존성을 제거해야만 한다.
...
모든 경우에 의존성은 명시적으로 퍼블릭 인터페이스에 노출된다. 이를 명시적인 의존성이라고 부른다.
...
다시 말해 의존성이 퍼블릭 인터페이스에 표현되지 않는다. 이를 숨겨진 의존성이라고 부른다.
처음에는 숨겨진 의존성이 좋은 것이라 여겼습니다. 저는 숨겨진 의존성을 일종의 감춰진, 즉 추상화라 생각을 하였지만 책의 내용을 다시 보면 퍼블릭 인터페이스에 의존을 하냐 마냐라 고 이야기하고 있으며 이럴 경우 명시적으로 의존을 하되 추상화된 클래스 혹은 인터페이스에 의존을 하는 것이 좋다 생각합니다.
new는 해롭다.
- new 연산자를 사용하기 위해서는 구체 클래스의 이름을 직접 기술해야 한다. 따라서 new를 사용하는 클라이언트는 추상화가 아닌 구체 클래스에 의존할 수밖에 없기 때문에 결합도가 높아진다.
- new 연산자는 생성하려는 구체 클래스뿐만 아니라 어떤 인자를 이용해 클래스의 생성자를 호출해야 하는지도 알아야 한다. 따라서 new를 사용하면 클라이언트가 알아야 하는 지식의 양이 늘어나기 때문에 결합도가 높아진다.
...
구체 클래스에 직접 의존하면 결합도가 높아진다는 사실을 기억하라.
...
new는 결합도를 높이기 때문에 해롭다.
...
사용과 생성의 책임을 분리하고, 의존성을 생성자에 명시적으로 드러내고 구체 클래스가 아닌 추상 클래스에 의존하게 함으로써 설꼐를 유연하게 만들 수 있다.
이전에 한번 다룬 CQS패턴과 비슷한 느낌이라 생각합니다. 또한 new는 자제하는 것이 맞지만 이미 의존성이 있다면 new를 통해 새로운 객체를 생성해도 된다 생각합니다.
표준 클래스에 대한 의존은 해롭지 않다
변경될 확률이 거의 없는 클래스라면 의존성이 문제가 되지 않는다.
책에서는 JDK에 포함된 클래스들 예를 들면 ArrayList를 예시를 들고 있습니다. 그러나 이는 프로젝틀르 진행하며 변경될 가능성이 낮은 클래스에 대해서 의존을 해도 괜찮다 생각합니다.
'책 > 오브젝트 (완)' 카테고리의 다른 글
10 상속과 코드 재사용 (0) | 2024.02.06 |
---|---|
09 유연한 설계 (0) | 2024.01.31 |
07 객체 분해 (1) | 2024.01.24 |
06 메시지와 인터페이스 (1) | 2024.01.23 |
5장 책임 할당하기 (0) | 2024.01.17 |