CH04 역할, 책임, 협력
협력이라는 문맥에서만 객체는 설계되어야 한다
위와 같은 소제목은 책에는 없다. 하지만 여러 사례와 문단에서 위와 같은 이야기를 계속 반복해서 하고 있어서 아예 소제목으로 뽑았다.
객체지향 설계의 전체적인 품질을 결정하는 것은 개별 객체의 품질이 아니라 여러 객체들이 모여 이뤄내는 협력의 품질이다. 훌륭한 객체지향 설계자는 객체들 간의 요청과 응답 속에서 창발하는 협력에 초점을 맞춰 애플리케이션을 설계한다. 협력이 자리를 잡으면 저절로 객체의 행동이 드러나고 뒤이어 적절한 객체의 상태가 결정된다.
앞에서도 그렇고 계속해서 객체를 설계할때는 상태보다 행동이 우선 고려되어야 한다고 강조되고 있다. 이번 장에서는 이 행동이 결국 객체들간의 협력이라는 커다란 문맥속에서 발생하는 것임을 강조하고 있다. 즉, 객체를 설계할 때 상태보다는 행동을, 행동을 고려할때는 결국 객체들간의 협력이라는 큰 문맥을 고려해야한다는 이야기를 하고 있다.
책임
일단 역할과 책임이라는 두 개념이 자꾸 섞여서 나오는데 둘의 구분을 다시 할 필요성이 있겠다. 책에서도 나왔듯이 역할이라는 개념이 더 넓은 개념이다. 역할은 책임들의 집합이다.
어떠한 역할을 할 객체가 필요한 상황일때 이 객체에게 결국 어떤 책임들을 부여할지를 설계하는 것이기 때문에 책임들의 집합이 역할이 된다는 것이 말이 된다.
... "객체 지향 개발에서 가장 중요한 능력은 책임을 능숙하게 소프트웨어 객체에 할당하는 것"이라고 말한다. 책임을 어떻게 구현할 것인가 하는 문제는 객체와 책임이 제자리를 잡은 후에 고려해도 늦지 않다. 객체와 책임이 이리저리 부유하는 상황에서 성급하게 구현에 뛰어드는 것은 변경에 취약하고 다양한 협력에 참여할 수 없는 비자율적인 객체를 낳게 된다.
계속 강조되고 있는 것이 냅다 구체적인 레벨에서의 고민을 하지 말고 객체간의 협력이라는 거시적 관점에서 어떤 객체에 어떤 책임을 할당할지에 관한 설계가 가장 중요하다는 것이다.
객체지향 설계는 협력에 참여하기 위해 어떤 객체가 어떤 책임을 수행해야 하고 어떤 객체로부터 메시지를 수신할 것인지를 결정하는 것으로부터 시작된다.
역할
역할은 협력을 추상화한 것이라고도 표현할 수 있다. 그래서 특정 역할을 수행 할 수 있는 객체들은 그들 간에 대체를 해줄 수 있다. 역할은 곧 협력의 추상화라고 할 수 있다.
책에서는 이렇게 이야기를 하고 있지만 코드레벨에서 이걸 생각해보면 바로 와닿지 않는다. 객체가 대체가 된다고? 좀 와닿지가 않는다. 그래서 구분을 잘 해서 받아들여야 한다. 일단 핵심을 먼저 이야기하자면 대체가 된다는 의미는 다형성의 차원에서 동일 타입인 덕분에 컴파일 에러 없이 처리가 된다는 의미이다. 런타임때 특정 작업에 대해서 특정 객체가 처리를 해야만 하는 상황인데 이걸 다른 객체가 대체할 수 있다는 이야기는 아니다.
딱 지금 시점에 내가 앱쪽에 작업중인 소셜로그인 기능을 예로 들어 보자. 나는 최상위에 추상클래스(원래 인터페이스를 쓰는데 dart는 인터페이스가 없다)를 아래와 같이 선언했다.
그리고 아래와 같이 이를 상속하는 여러 객체를 만들었다.
책에서 말하고 있는 맥락에서 이 예시를 설명하면 GoogleAuthenticationManager와 AppleAuthenticationManager 는 같은 역할을 수행할 수 있는 객체들이다. 즉, 대체가 가능하다고 할 수 있다. 그래서 다형성의 장점을 누리면서 루즈 커플링을 실현할 수 있다.
최상위 클래스에서 협력을 추상화해두었고 이를 상속하는 구체화된 클래스들에서 각자 필요에 맞게 이 행동에 대해서 처리를 자율적으로 하고 있는 상황이기 때문이다.
하지만 구분해서 인지해둘 것은 책에서 말하는 '대체 가능성'이라는 것이 무조건 그냥 '이 객체가 해주는 건 저 객체도 해줄 수 있는거야' 이런 개념이 아니라는 것이다. 아래와 같이 다형성의 관점에서 대체 가능하다는 표현을 쓰는 것이다. 개인적으로 이 표현이 혼란을 주는 것 같아서 대체 가능하다는 말 자체를 쓰지 않는게 더 좋은 것 같다.
아래 부분이 핵심이다.
_findSocialAuthenticationManager 의 결과로 누가 authenticationManager 로 정해지든 간에 결국 getSocialAuthentication() 라는 행동을 그 어떤 객체든 수행해줄 수 있다는 것이다.
협력을 따라 흐르는 객체의 책임
계속해서 객체의 설계는 무조건 협력이라는 큰 문맥 아래에서 이뤄져야한다는 것을 강조하고 있다.
올바른 객체를 설계하기 위해서는 먼저 견고하고 깔끔한 협력을 설계해야 한다. 협력을 설계한다는 것은 설계에 참여하는 객체들이 주고받을 요청과 응답의 흐름을 결정한다는 것을 의미한다. 이렇게 결정된 요청과 응답의 흐름은 객체가 협력에 참여하기 위해 수행될 책임이 된다.
일단 객체에게 책임을 할당하고 나면 책임은 객체가 외부에 제공하게 될 행동이 된다. 협력이라는 문맥에서 객체가 수행하게 될 적절한 책임, 즉 행동을 결정한 후에 그 행등을 수행하는 데 필요한 데이터를 고민해야 한다.
계속 같은 내용을 반복해서 강조하고 있다. 객체의 설계에 있어 객체의 상태는 우선순위중 제일 마지막에 있다. 중요하지 않다는 이야기이다.
결론적으로 객체의 설계는 협력이라는 문맥 하에서 이뤄져야한다는 것인데 이 말에서 더 중요한 포인트는 결국 '협력'부터 정의를 해야한다는 것이다. 결국 큰 틀에서 소프트웨어가 해결해야할 도메인이 무엇이며 이를 위해서 어떤 협력을 이뤄내야할지부터 정의가 되어야하고 그렇게 협력이 정의 되어야 세부적으로 어떤 객체들이 어떤 협력을 해야할 지 생각할 수 있을 것이다.
디자인 패턴
디자인 패턴에 관해서 되게 재미있는 표현을 책에서 사용하고 있다.
일반적으로 디자인 패턴은 반복적으로 발생하는 문제와 그 문제에 대한 해법의 쌍으로 정의 된다. 패턴은 해결하려고 하는 문제가 무엇인지를 명확하게 서술하고, 패턴을 적용할 수 있는 상황과 적용할 수 없는 상황을 함께 설명한다. 패턴은 반복해서 일어나는 특정한 상황에서 어떤 설계가 왜 더 효과적인지에 대한 이유을 설명한다.
디자인 패턴은 공통으로 사용할 수 있는 역할, 책임, 협력의 템플릿이다. 디자인 패턴은 책임-주도 설계의 결과물인 동시에 지름길이다.
개인적으로 이 표현들이 되게 마음에 들었고 확 와닿았다. 디자인 패턴 강의를 들을때 재미있게 듣긴 했지만 주된 원동력은 의무감이었는데 이 책을 보고, 디자인 패턴에 대한 저자의 이러한 표현들을 보면서 다음번에 제대로 디자인 패턴에 대해서 정리를 해야겠다는 의욕이 생겼다.
특히 제일 좋았던 것은 디자인 패턴이 해법의 쌍이라는 것이다. 예전에는 그저 '이러한 패턴이 있다. 이럴때 써먹어라' 이런 관점에서만 공부를 했는데 저자의 표현을 보니 디자인 패턴은 특정 상황에 대한 '솔루션' 인 것이었다. 내가 무엇인가를 구현함에 있어서 '어떻게 구현하지~~' 고민할 때 이미 그 상황에 대한 해결책은 대부분 이미 설계되어 있는 디자인 패턴 중 하나일 가능성이 크다.
다음번에 디자인 패턴 공부를 할 때는 기계적으로 보지 말고 좀 제대로 공부를 하자.
Last updated