본문 바로가기

책/오브젝트 (완)

5장 책임 할당하기

4장에서는 책임중심의 접근법이 아닌 데이터중심의 접근법의 문제점을 짚어보았습니다. 그리고 5장에서는 어떻게 해야 책임을 잘 할당할 수 있는지를 언급하였습니다. 기억에 남는 접근법은 총 두개가 있는데 하나는 처음에 설계를 할 때부터 GRASP 패턴으로 접근하는것 다른 하나는 절차지향적으로 코드를 먼저 작성하고 이를 리팩토링 하는 것입니다. 다행히도 GRASP 패턴의 경우 이미 1~4장까지 언급한 내용과 동일하다는 것입니다.

 

객체에게 할당된 책임이 협력에 어울리지 않는다면 그 책임은 나쁜 것이다. 객체의 입장에서는 책임이 조금 어색해 보이더라도 협력에 적합하다면 그 책임은 좋은 것이다. 책임은 객체의 입장이 아니라 객체가 참여하는 협력에 적합해야 한다.

이는 "객체지향의 사실과 오해"라는 책에서도 나오는 대목과도 비슷한 면이 있다 생각합니다. 현실 세계에서는 티켓이 스스로 가격을 알려준다거나 음료가 스스로 자신의 양을 줄이는 일은 어색하지만 객체지향의 세계에서는 충분히 합리적인 일입니다. 그리고 이는 협력의 관점에서 본다면 음료는 자신의 음료량을 스스로 줄이기에 이 역시 충분히 합리적인 책임이라 생각합니다.

 

객체가 메시지를 선택하는 것이 아니라 메시지가 객체를 선택하게 해야 한다.

이는 책임중심의 접근법과 동일합니다. 객체를 생각하고 객체가 해야 하거나 할 수 있는 행동에 대해 생각하지 않고 해야 하는 "행동"에 먼저 초점을 맞추고 이에 맞는 객체를 선택하는 방법입니다.

 

GRASP은 "General Responsibility Assignment Software Pattern"(일반적인 책임 할당을 위한 소프트웨어 패턴)의 약자로 객체에게 책임을 할당할 때 지침으로 삼을 수 있는 원칙의 집합을 패턴형식으로 정리한 것이다.

저는 이 책에서 처음으로 GRASP패턴에 대해 알게 되었으나 여러 프로젝트를 진행하며 생각했던 혹은 본받고자 한 코드의 기능을 모아놓은 것이었습니다. 그래도 여전히, 충분히 중요하다는 사실을 알기에 추후 글로 다시 한번 정리하고자 합니다.

 

설계를 시작하기 전에 도메인에 대한 개략적인 모습을 그려 보는 것이 유용하다.

프로젝트에 접합해 생각을 해본다며 클래스에 대해 고민하는 것이 아닌 프로젝트에서 제공해야하는 기능은 무엇이며 이와 연관되어 있는 기능들은 무엇이 있을까에 대한 고민이 필요하다는 말이라 생각합니다. 그렇기에 포스팅을 하는 지금 시점으로 GDSC 솔루션 챌린지에서 진행하는 프로젝트를 하기 이전 도메인에 대한 개략적인 모습을 그려보고자 합니다.

 

객체의 책임과 책임을 수행하는 데 필요한 상태는 동일한 객체 안에 존재하야 한다. 따라서 객체에게 책임의 책임을 할당하는 첫번째 원칙은 책임을 수행해야 할 정보를 알고 있는 객체에게 책임을 할당하는 것이다.
...
책임을 수행하는 객체가 정보를 "알고"있다고 해서 그 정보를 "저장"하고 있을 필요는 없다. 객체는 해당 정보르 제공할 수 있는 다른 객체를 알고 있거나 피요한 정보를 계산해서 제공할 수도 있따. 어떤 방식이건 정보 전문가가 데이터를 ㅈ반드시 저장하고 있을 필요는 없다는 사실을 이해하는 것이 중요하다.

이를 GRASP에서는 INFORMATION EXPERT(정보 전문가)패턴이라 합니다. 이는 이전 장에서 꾸준히 언급한 내용으로 "데이터와 프로세스를 한 곳에서 관리를 한다."정리할 수 있을것 같습니다. 다만 설계를 할 때 '절대'라는 것은 없으므로 참고하는 정도로 여기고자 합니다.

 

책임을 할당할 수 있는 다양한 대안들이 존재한다면 응집도와 결합도의 측면에서 더 나은 대안을 선택하는 것이 좋다. 다시 말해 두 협력 패턴 중에서 높은 응집도와 낮은 결합도를 얻을 수 있는 걸계가 있다면 그 설계를 선택해야 한다는 것이다.

GRASP에서는 이를 LOW COUPLING(낮은 결합도) 패턴과 HIGH COHESION(높은 응집도)패턴이라고 부른다.

아마도 객체지향을 처음 배웠을때부터 낮은 결합도 높은 응집도가 언급되었던것 같습니다. 지금까지 결합도와 응집도를 설계를 처음 시작할 때 고려하는 것에 불과했지만 책에서 언급한 덕분에 설계및 구현을 하는 방법이 여럿일때 이를 떠올리며 최대한 변경에 용이한 설계를 할 수 있을듯 합니다.

 

이미 결합돼 있는 객체에게 생성 책임을 할당하는 것은 설계의 전체적인 결합도에 영향을 미치지 않는다. 결과적으로 CREATOR 패턴은 이미 존재하는 객체 사이의 관계를 이용하기 때문에 설계가 낮은 결합도를 유지할 수 있게 한다.

즉 이미 어느정도 결합되어 있다면 해당 객체에서 결합되어 있는 객체를 생성하는 책임을 주자는 의견입니다. 저의 짧은 소견으로 는 DTO 정도 혹은 빌더 패턴으로 새로운 객체를 생성해 저장하는 로직정도에 사용될수 있는 패턴이라 생각합니다. 즉 생성만을 위해 해당 객체를 알게 되면 추가적인 결합도가 생기기에 주의하자는 것이 이 글의 요지인듯 합니다.

 

클래스의 속성이 서로 다른 다른 시점에 초기화되거나 일부만 초기화된다는 것은 응집도가 낮다는 증거다. 따라서 함께 초기화되는 속성을 기준으로 코드를 분리해야 한다.

이는 리팩토링의 기준으로 삼아도 된다 생각합니다. 예를 들어 하나의 객체를 생성할 때 여러 속성들이 서로 다를 때 초기화가 된다면 해당 속성들을 하나의 객체로 묶는 것이 조금 더 좋은 설계를 의미합니다.

 

메서드들이 인스턴스 변수를 사용하는 방식을 살펴보는 것이다. 모든 메서드가 객체의 모든 속성을 사용한다면 클래스의 응집도는 높다고 볼 수 있다. 반면 메서드들이 사용하는 속성에 따라 그룹이 나뉜다면 클래스의 응집도가 낮다고 볼 수 있다.
...
이 경우 클래스의 응집도를 높이기 위해서는 속성 그룹과 해당 그룹에 접근하는 메서드 그룹을 기준으로 코드를 분리해야 한다.

이는 또 다른 리팩토링의 기준으로 삼아도 된다 생각합니다. 객체안 존재하는 데이터를 공평하게 모든 메소드에서 사용한다면 괜찮겠지만 특정 속성만을 사용하는 메서드가 존재하고 이러한 메소드들이 여럿 존재할 경우 이들을 분리할 방법을 생각해야 합니다.

 

객체의 타입에 따라 변하는 로직이 있을때 변하는 로직을 담당할 책임을 어떻게 할당해야 하는가? 타입을 명시적으로 정의하고 각 타입에 다형적으로 행동하는 책임을 할당하라.
...
POLYMORPHISM 패턴은 객체의 타입을 검사해서 타입의 따라 여러 대안들을 수행하는 조건적인 논리를 사용하지 말라고 경고한다. 대신 다형성을 이용해 새로운 변화를 다루기 쉽게 확장하라고 권고한다.

책이 저에게 다형성(POLYMORPHISM)은 이러할 때 사용하라 말하는 것 같습니다. 즉 변화할 수 있으며 어떤 로직이 들어올때 해당 로직이 수행되는 내용이 다르다면 다형성을 사용하라는 말로 해석됩니다.

 

PROTECTED VARIATIONS 패턴은 책임 할당의 관점에서 캡슐화를 설명한 것이다. "설계에서 변하는 것이 무엇인지 고려하고 변하는 개념을 캡슐화하라"라는 객체지향의 오랜 격언은 PROTECTED VARIATIONS 패턴의 본질을 잘 설명해준다. 우리가 캡슐화해야 하는 것은 변경이다. 변경이 될 가능성이 높은가? 그렇다면 캡슐화하라.

이 역시 다형성을 설명하는 것과 뜻을 같이한다 생각합니다.

 

이처럼 이해하기 쉽고 수정하기 쉬운 소프트웨어로 개선하기 위해 겉으로 보이는 동작은 바꾸지 않은 채 내부 구조를 변경하는 것을 리팩터링이라고 부른다.

리팩토링의 정의를 저자는 다음과 같이 내리고 있습니다. 즉 외부에 노출되어 있는 인터페이스는 수정하지 않고 내부 로직을 개선하는 것을 의미합니다. 리팩토링의 대상은 가독성, 성능이 있을 것이라 생각합니다.

 

객체로 책임을 분배할 때 가장 먼저 할 일은 메서드를 응집도 있는 수준으로 분해하는 것이다.

해당 파트는 책의 저자가 리팩토링을 하는 방법에 대해 언급하는 대목으로 리팩토링을 할 때 먼저 하나의 큰 메소드를 응집도 있는 수준으로 구체적으로 설명하면 "서로 같은 책임, 같은 속성들을 사용하는"이 될 것 같습니다.

 

한 가지 일에 집중하는 응집도 높은 메서드는 변경 가능한 설계를 이끌어 내는 기반이 된다. 이런 메서드들이 하나의 변경 이유를 가지도록 개선될 때 결과적으로 응집도 높은 클래스가 만들어진다.

메서드의 책임을 나눌때는 이유를 가지고 나누어야 한다 생각합니다. 이유없이 그저 메소드를 작게 나누기 위해서만 메소드를 나누면 안된다 생각합니다.

 

 

' > 오브젝트 (완)' 카테고리의 다른 글

07 객체 분해  (1) 2024.01.24
06 메시지와 인터페이스  (1) 2024.01.23
4장 설계 품질과 트레이드오프  (0) 2024.01.16
3장 역할, 책임, 협력  (1) 2024.01.15
2장 객체 지향 프로그래밍  (0) 2024.01.11