[Ch.5] 책임 할당하기

신은지·2022년 11월 28일
0


오브젝트 : 코드로 이해하는 객체지향 설계 (조영호 저) 를 읽고 정리합니다.


GRASP 패턴으로 알아보는 책임 할당 기준

어떤 객체에게 어떤 책임을 할당해야할까? 🧐

1️⃣ 책임 주도 설계 리마인드

  • 객체의 책임과 협력에 초점 맞추기
    1. 데이터보다 행동을 먼저 결정할 것
      : 협력에 참여하기 위해, 외부에 제공하는 행동(= 협력 속에서 수행할 책임)을 먼저 결정해야 한다.
    2. 협력이라는 문맥 안에서 책임을 결정할 것
      : 책임은 객체가 아닌 객체가 참여하는 협력에 적합해야 한다.
      : 메시지를 전송하는 클라이언트의 의도에 적합하도록, 메시지를 결정한 후 객체를 선택해야 한다.
  • 책임 주도 설계의 흐름
    • 책임을 결정한 후, 책임을 수행할 객체를 결정하기
      1. 시스템이 사용자에게 제공해야 하는 기능, 전체 시스템 책임 파악
      2. 시스템 책임을 더 작은 책임으로 분할
      3. 분할된 책임을 수행할 수 있는 적절한 객체 또는 역할을 찾아 책임 할당
      4. 객체가 책임을 수행하는 중 다른 객체 도움이 필요한 경우, 해당 도움을 수행할 적절한 객체 또는 역할 찾기
      5. 찾은 객체 또는 역할에게 책임 할당 = 두 객체의 협력 관계 구축 완료

2️⃣ GRASP 패턴?

일반적인 객체 책임 할당을 위한 소프트웨어 패턴.

👣 도메인 개념에서 출발하기

  • 설계를 시작할 때, 도메인에 대한 개략적인 모습을 그려보면 좋다.
    • 도메인에 존재하는 개념들을 책임 할당의 대상으로 사용하자.
  • 이때, 도메인을 완벽하게 정리하는 것에 집착하지 말자!
    • 단순히 출발점의 목적으로 활용할 것
    • 책임을 할당받을 객체들의 종류와 관계에 대한 정보를 수집하는 선으로 족하다.
  • 올바른 도메인 모델?
    • 그런건 없다..! 단지 구현에 도움이 되는 실용적인 모델이 필요할 뿐이다.

🦾 정보 전문가에게 책임 할당하기

  • 책임 수행에 필요한 메시지를 결정하자.
    • 메시지를 전송할 객체의 의도를 반영하여 아래 질문을 생각하며 메시지를 결정해야 한다.
      • 메시지를 전송할 객체는 뭘 원할까?
      • 메시지를 수신할 객체는 누가 적당할까?
  • 메시지 수신 객체는 캡슐화의 단위에서 결정해야 한다.
    • INFORMATION EXPERT (정보 전문가) 패턴
    • 책임을 수행할 정보를 알고 있는 객체에게 책임을 할당하자.

정보 전문가 패턴

  • 객체가 자신이 소유한 정보와 관련된 작업을 수행하는 직관
    • 이때, 정보 전문가는 데이터를 반드시 저장할 필요가 없다는 점에 주의
      • 정보를 제공할 수 있는 다른 객체를 알거나, 필요한 정보를 직접 계산해서 제공할 수 있기 때문
  • 따라서 상태와 행동을 함께 가지는 단위(=객체)를 책임 할당 관점에서 표현한다.

⛑ 높은 응집도, 낮은 결합도

  • 위 과정대로 했는데, 동일한 기능을 구현할 수 있는 여러 설계 선택지가 있다면?
    • 응집도를 높이고 결합도를 낮게 만들 수 있는 설계를 선택하자.
  • 높은 응집도 패턴 (HIGH COHESION)
    • 어떤 협력 관계를 만들어야 각 객체의 변경 책임을 분리할 수 있을까?
  • 낮은 결합도 패턴 (LOW COUPLING)
    • 어떤 협력 관계를 만들어야 설계 전체적으로 결합도를 추가하지 않을 수 있을까?

⚒ 창조자에게 객체 생성 책임 할당하기

  • 창조자 패턴 (CREATOR)
    • 객체 A를 생성할 때, 아래 조건을 최대한 많이 만족하는 B에게 책임을 할당하라
      • B가 A 객체를 포함하거나 참조한다
      • B가 A 객체를 기록한다
      • B가 A 객체를 긴밀하게 사용한다
      • B가 A 객체를 초기화하는 데 필요한 데이터를 가지고 있다 (= B는 A에 대한 정보 전문가 )

구현을 통해 설계 검증하기

설계가 협력과 책임을 잘 반영했는지 검증하기 위해서 코드를 작성하고 실행해봐야 한다.

1️⃣ 클래스 응집도 판단하기

메세지가 객체를 선택해야 한다!

  • 객체가 협력하는 객체의 내부 구현에 대해 지식이 없을 때, 각 객체의 깔끔한 캡슐화가 가능하다.
  • 객체간 연결고리가 메시지 뿐이기 때문에, 메시지가 변경되지 않는 한 내부 구현은 영향을 받지 않기 때문
  • 변경에 취약한 클래스가 존재하는가?
    • 변경에 취약한 클래스 : 코드를 수정해야 하는 이유가 하나 이상인 클래스
    • 높은 응집도를 위해 변경의 이유에 따라 클래스를 분리해야 한다.
      • 따라서 설계 개선 작업은 변경의 이유가 여러개인 클래스가 있는지 확인하면서 시작하면 좋다.
    • 변경의 이유를 파악하는 방법
      • (1) 인스턴스 변수 초기화 시점 파악
        : 클래스 속성이 서로 다른 시점에 초기화되거나 Or 일부만 초기화된다 = 응집도가 낮다
        : 함께 초기화되는 속성을 기준으로 코드를 분리하자!
      • (2) 메서드들이 인스턴스 변수를 사용하는 방식 파악
        : 메서드가 객체의 모든 속성을 사용할 때 클래스 응집도가 높다.
        : 속성 그룹과 해당 그룹에 접근하는 메서드 그룹을 기준으로 코드를 분리하자22!

2️⃣ 설계 개선하기

⛑ 타입 분리하기

  • 독립적인 타입은 서로 다른 클래스로 분리하자
    • 자신의 인스턴스 변수를 모두 함께 초기화 가능
    • 클래스에 있는 모든 메서드는 동일한 인스턴스 변수 그룹을 사용

타입 분리로 인해 발생할 수 있는 문제들 🔨

  • 특정 클래스 응집도는 높아져도, 변경과 캡슐화 관점에서 보면 전체 설계 품질은 나빠지는 케이스
    • 전체 설계의 결합도가 높아질 수 있다
    • 새로운 기능을 추가하기 위해 여러 클래스에 접근해야 한다

⛑ 다형성을 통해 분리하기

  • 역할을 이용해 객체 타입을 추상화하자
    • 동일한 책임을 수행한다 = 동일한 역할을 수행한다. 따라서 역할에 대해 결합하게 의존성을 제한하자
    • 역할을 대체할 클래스들 사이의 관계에 따라 추상화 방법을 골라보자 (Java)
      • (1) 클래스들 사이서 구현을 공유해야 함 = 추상 클래스
      • (2) 구현 공유할 필요 없이 책임만 정의하면 됨 = 인터페이스
  • POLYMORPHISM (다형성) 패턴
    • 객체 타입에 따라 변하는 행동이 있다면? 타입을 분리하고, 변하는 행동을 각 타입의 책임으로 할당하자
    • if-else가 아니라, 객체의 타입에 따라 변화를 컨트롤하는 방식

⛑ 변경으로부터 보호하기

  • PROTECTED VARIATIONS (변경 보호) 패턴
    • 변경을 캡슐화하도록 책임을 할당하는 방식
      • 변화가 예상되는 불안정한 지점들을 클래스로 인터페이스를 이용해 변경을 캡슐화한다

캡슐화를 이용해 설계 결합도와 응집도 향상하기
(1) 하나의 클래스가 여러 타입의 행동을 구현하는 것 같다 = 다형성 패턴으로 책임 분산
(2) 예측 가능한 변경으로 인해 클래스들이 불안정해진다 = 변경 보호 패턴으로 인터페이스를 이용한 캡슐화


3️⃣ 변경과 유연성

클래스가 작고 한 가지 일만 수행할 때, 책임이 적절하게 분배되어 있을 때, 다른 클래스와 느슨하게 결합되어 있을 때 변경에 유연한 코드를 만들 수 있다.

  • 개발자가 변경에 대비하는 두 가지 방법
    • (1) 코드를 수정하기 쉽도록 최대한 단순하게 설계하기
    • (2) 코드를 수정하지 않고도 변경을 수용할 수 있게 유연하게 설계하기
  • 유연성은 요소들 사이의 의존성에 의해 결정된다

책임 주도 설계의 대안

적절한 책임을 찾는게 어렵다면, 일단 코드부터 짜고 이후 리팩토링을 통해 구조를 개선해보자.

  • 리팩토링 (Refactoring)
    • 겉으로 보이는 동작은 바꾸지 않은 채 내부 구조를 개선(변경)하는 것

1️⃣ 메서드 응집도

  • 긴 메서드 = 몬스터 메서드 (monster method)
    • 문제
      • 코드의 전체적인 컨텍스트 파악이 오래 걸린다
      • 코드 변경할 때 수정해야 할 부분 찾기가 어렵다
      • 메서드 내부 일부 로직을 수정했을 때, 다른 로직이 영향을 받을 가능성이 크다
      • 로직의 일부를 재사용하는 것이 불가능하다
      • 코드 재사용을 위해 복붙하게 되어, 코드 중복이 발생하기 쉽다
    • 요약
      • 응집도가 낮아서 보기도 어렵고 쓰기도 어렵고 재활용도 어렵고 어쨌든 모든게 어려워진다!
    • 주석이 주렁주렁 필요하다? 높은 확률로 메서드 응집도가 낮은 경우!
      • 메서드를 작게 분해해서 응집도를 높이자. 작은 메서드는 일종의 주석과 같다
  • 메서드를 작고 응집도 높은 메서드로 분리할 때, 각 메서드를 적절한 클래스로 이동 (책임 할당)하기가 더 쉬워진다.

2️⃣ 자율적인 객체 만들기

  • 메서드가 사용하는 데이터를 가지고 있는 클래스로 메서드를 이동시키자
    • 자율적인 객체 = 데이터를 처리하기 위해 타 객체에게 의존할 필요가 없는 객체 = 내가 가진 데이터를 스스로 처리하는 객체
    • 메서드를 이동할 때는 캡슐화, 응집도, 결합도의 측면에서 해당 이동이 적절한지 판단한다.
profile
호그와트 장학생

0개의 댓글