오브젝트

오브젝트라는 책을 한번 읽고나서 객체지향 프로그래밍의 윤곽이 조금은 잡혔다. 하지만 얽히고 섥힌 내용을 실제 설계에 적용하는데는 어려움이 있었다. 책임, 결합도, 응집도 등의 단어만 머리속에 맴돌고 어떻게 설계를 시작해야될지 감이 잡히지 않았다.

두 번째로 책을 다시 읽으면서 기존의 흐릿했던 개념들을 정리하고 실제 설계에 써먹을 수 있는 방법으로 내용을 압축해보고자 한다.

챕터 별 내용 요약

챕터 별 내용 요약

저자가 어떤 순서대로 책을 썻는지 추측해보는 것이 전체적인 객체지향 설계의 숲을 바라보는데 유리할 것 같다.

  • chap01 - 소프트웨어의 기본적인 목적은 제대로 동작하고, 이해하기 쉽고, 변경에 용이한 소프트웨어다. 우리들의 코드가 제대로 동작하지만, 이해하기 어렵고 변경에 용이하지 못한 이유를 알아본다.
  • chap02 - 이 책에서 다루게 될 객체지향의 다양한 주제들을 얕게 알아보는 장이다.
  • chap03 - 협력과, 책임, 역할의 개념에 대해서 설명하고 책임 주도 설계에 대해 소개한다.
  • chap04 - 설계 품질 척도인 캡슐화, 결합도, 응집도에 대해서 설명하고 데이터 주도 설계가 가지는 한계점에 대해 설명한다.
  • chap05 - chap03, chap04에서 책임 주도 설계와 데이터 주도 설계의 한계에 대해 설명했다. 이 장에서는 책임 주도 설계 시 좋은 책임 할당 을 위한 GRASP 패턴에 대해 소개한다.
  • chap06 - 책임 할당 방법에 따라 책임 할당을 마쳤다면 인터페이스를 설계해야 한다. 인터페이스와 메시지는 무엇인지 설명하고 좋은 인터페이스를 만들기 위한 4가지 원칙에 대해 설명한다.
  • chap07 - 프로그래밍 패러다임의 변화를 알아보며 기존 패러다임의 문제점에 따른 발전 방식을 알아보고 객체지향의 진정한 의미에 대해 알아본다.
  • chap08 - 의존성의 개념과 의존성과 결합도의 관계에 대해 설명한다. 그리고 의존성 관리의 기법으로 구현 레벨에서 어떻게 유연하고 재사용 가능하도록 시스템을 변경시키는지 설명한다.
  • chap09 - chap08에서 설명했던 의존성 관리 기법들을 원칙이라는 관점에서 설명한다.
  • chap10 - 중복 코드 관점에서 상속을 알아보고 상속으로 발생하는 문제를 알아본다. 그리고 어떻게 상속을 이용해야 재사용 가능한 설계를 할 수 있는지 알아본다.
  • chap11 - 상속으로 일어났던 취약한 기반 클래스 문제, 조합 폭발 문제를 합성을 통해 해결할 수 있음을 알아본다.
  • chap12 - 다형성이 구현되는 기술적인 메커니즘을 살펴본다.
  • chap13 - 타입 계층을 올바르게 구성할 수 있는 원칙을 살펴본다.
  • chap14 - 변경되는 것과 변경되지 않는 것을 구분해 설계에 일관성을 부여하는 방법을 알아본다.
  • chap15 - 디자인 패턴과 프레임워크의 개념에 대해 알아본다.

전체적인 내용 정리

설계 방법

우리는 제대로 동작하고 변경에 용이하며 이해하기 쉬운 코드를 작성해야 한다. 제대로 동작하는 코드를 작성하기는 쉽지만 변경에 용이하고 이해하기 쉬운 코드를 작성하는 것은 어렵다. (이유가 궁금하다면 chap01 정리를 참고하자.)

그 이유는 우리가 코드를 작성할 때 협력, 책임, 역할에 대해 고민하지 않고 데이터 중심의 설계를 했기 때문이다. 협력, 책임, 역할에 대해 고민하지 않은 코드는 응집도가 낮고 결합도가 높은 코드가 된다. (협력, 책임, 역할의 의미가 궁금하다면 chap03 정리를 참고하자.)

객체는 ‘하는 것’과 ‘아는 것’에 대한 책임을 가져야 한다. 그리고 이 책임이 모여 역할이 된다. 그리고 클라이언트 객체는 이 객체에게 메시지를 전송해 서로 협력하게 된다.

문제점은 우리가 객체를 만든다는 점이다. 객체는 책임의 집합이다. 객체에 부여될 책임도 우리가 나누어야 한다. 여기서 시스템 책임과 도메인 모델로부터 책임 주도 설계 방법이 시작된다. (기존의 데이터 중심 설계 방법의 한계를 알고싶다면 chap03 정리를 참고하자.)

방법은 다음과 같다.

  1. 시스템이 사용자에게 제공해야하는 기능인 시스템 책임을 파악한다.
  2. 도메인 모델에 있는 개념들을 후보로 선택해 직접 책임을 할당한다.
  3. 시스템 책임을 더 작은 책임으로 분할한다.
  4. 이 때, 분할된 책임을 어디로 할당할 것인지 고민하는데 적절한 객체를 찾아 할당한다. 이 때, 필요한 지식과 방법을 가장 잘 알고 있는 정보 전문가 객체에게 책임을 할당하고(응집도와 결합도를 고려하여), 기존의 객체에서는 메시지를 통해 협력을 요청한다. 이 과정에서 협력이라는 문맥이 생성된다.
  5. 책임을 점점 세분화 하면서 협력을 정제시킨다. 이 과정에서 협력이 유사한 구조를 가진다면 객체를 포괄할 수 있는 역할을 고려해 객체를 역할로 대체한다.
  6. 책임의 할당과 정제를 통해 객체(역할)의 행동과 상태를 결정할 수 있게된다.

설계 품질을 판단하는 척도에는 캡슐화, 응집도, 결합도가 있다. 위와 같은 과정을 완벽하게 수행해내면 우리는 좋은 설계 품질을 얻을 수 있다.(캡슐화, 응집도, 결합도의 의미가 궁금하다면 chap04 정리를 참고하자.)

책임 할당

그렇다면 책임 할당을 어떻게 하느냐가 중요할 것이다. GRASP 패턴은 객체에게 책임을 할당할 때 지침으로 삼을 수 있는 원칙들의 집합이다. (상세한 내용은 chap05 정리를 보고 GRASP 패턴을 익히자.)

  • 정보 전문가 패턴
  • 낮은 결합도 패턴
  • 높은 응집도 패턴
  • 창조자 패턴
  • 다형성 패턴

위와 같이 5개의 패턴이 존재한다. 어떤 객체에게 책임을 할당할것인지 모호하다면 위 패턴을 참고하자.

인터페이스 설계

이제 적절한 객체에게 적절한 책임을 할당했으면 협력의 매개체인 인터페이스를 설계해야 한다. 인터페이스 설계를 올바르게 하도록 도움을 줄 수 있는 4가지 법칙이 있다.

  • 디미터 법칙
  • 묻지 말고 시켜라
  • 의도를 드러내는 인터페이스
  • 명령-쿼리 분리

위의 법칙들의 함정에 빠지지 않고 잘 활용한다면 훌륭하게 인터페이스를 설계할 수 있을 것이다. (추가적인 내용이 알고 싶다면 chap07 정리를 참고하자)

의존성 관리

이제 설계가 어느정도 완성되었다면 살짝은 구현의 영역으로 넘어가보자. 객체가 꼭 필요한 객체에게만 의존하고 있는가? 과한 의존성은 변경을 방해한다. 만약 구체 클래스에 의존하고 있다면 필연적으로 해당 구체 클래스에 대해서 더 많은 지식을 알아야 한다.

  • 구체 클래스에 대한 의존성 제거
  • new 지양하기

위와 같은 의존성 관리 기법을 지킨다면 변경에 유용한 설계로 거듭날 수 있다. (더욱 자세한 내용은 chap08 정리를 참고하자)

위의 기법들을 원칙이라는 관점에서 다시 정리해보면 다음과 같다.

  • 개방-폐쇄 원칙
  • 생성 사용 분리
  • 의존성 주입
  • 의존성 역전 원칙

특히 의존성 역전 원칙은 패키지를 어떻게 구성할 것인지에 대한 인사이트를 준다.(더욱 자세한 내용은 chap09 정리를 참고하자)

상속 제대로 사용하기

우리는 중복 코드를 제거하기 위해 상속을 사용해왔다. 하지만 중복 코드를 제거하기 위한 상속은 ‘취약한 기반 클래스 문제’를 야기한다. (‘취약한 기반 클래스 문제’에 대해 더욱 자세한 내용은 chap10 정리를 참고하자)

중복 코드를 제거하기 위한 상속은 일정 부분 합성으로 대체될 수 있다. 중복 코드 제거를 위한 상속에서 일어났던 대부분의 문제를 해결한다. (조합을 통해 문제점을 해결하는 자세한 방법은 chap11 정리를 참고하자)

하지만 상속이 꼭 필요한 경우도 있다. 타입 계층을 만들어야 하는 경우이다. 주의할 점은 클라이언트의 기대에 따라 타입 계층을 분리해야 한다는 것이다. (자세한 이유는 chap13 정리를 참고하자)

만약 나눠진 타입 계층이 서로 비일관적인 방식으로 책임을 구현해 코드의 가독성이 떨어지는 경우가 있다. 타입 계층의 구현이 서로 일관성을 유지한다면 이해하기 쉬운 코드가 만들어지고, 일관적인 협력 방식을 강제하므로 새로운 타입 계층의 추가에도 일관적인 협력 방식을 취할 수 있다.

변하지 않는 부분과 변하는 부분을 구분해 협력 방식을 재설계하자. (변하는 부분을 분리해 적절한 협력 방식을 구축하는 방법은 chap14 정리를 참고하자)

결론

위의 방법을 순서대로 적용하고 다시 처음부터 적용해 설계를 정제하자. 많은 프로젝트를 하며 설계에 대한 경험을 쌓고 디자인 패턴을 익혀 협력 방식을 순간적으로 떠올리고 적용할 수 있는 능력을 갖추자.

태그:

카테고리:

업데이트:

댓글남기기