본문 바로가기
카테고리 없음

오브젝트 5장 6장 끄적거림

by HaningYa 2022. 1. 28.
728x90

5장 책임 할당하기

데이터 중심 설계로 인해 발생할 수 있는 문제점의 해결책: 책임에 초점을 맞추는 것

그렇다면 어떤 객체에게 어떤 책임을 줄까? -> 상황과 문맥에 따라 변동
-> 따양한 관점에서 설계를 평가

  • 데이터 보다 행동을 먼저 결정
  • 협력이라는 문맥 안에서 책임을 결정

데이터 중심: 이 객체가 포함해야 하는 데이터가 무엇인가 -> 데이터를 처리하는 데 필요한 오퍼레이션은 무엇인가
책임 중심: 이 객체가 수행해야 하는 책임은 무엇인가 -> 이 책임을 수행하는 데 필요한 데이터는 무엇인가

협력이라는 문맥 안에서 책임을 결정

메시지를 전송하는 클라이언트의 의도에 적합한 책임을 할당

메시지를 결정한 후에 객체를 선택해야 한다. (메시지가 객체를 선택해야 한다.)

메세지는 클라이언트의 의도를 표현한다.

객체를 결정하기 전에 객체가 수신할 메시지를 먼저 결정한다.

-> 객체입장에서는 어떤 메시지가 올지 모르기 떄문에 전송자 관점에서 메시지 수신자가 캡슐화 된다.

설계: 트레이트 오프 활동 -> 많은 설계중 한가지를 선택해야함

많은 방법 중 최선의 방법을 선택하는 방식? 변경 범위의 최소? 핵심보단 종단 코드의 변경? 사이드이펙트가 적은 영역의 코드?

#협력 #메시지 #책임

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

1. 도메인 개념에서 출발하기: 도메인의 대한 개략적인 모습
2. 정보 전문가에게 책임 할당 (*정보 전문가 객체는 그 정보를 '알고'있는거지 '저장'해야하는건 아니다.
-> 객체란 상태와 행동을 함께 가지는 단위라는 객체지향의 가장 기본적인 원리를 책임 할당의 관점에서 표현
3. 높은 응집도와 낮은 결합도: 여러 설계중에서 높은 응집도와 낮은 결합도를 얻을 수 있는 설계를 선택
4. 창조자에게 객체 생성 책임을 할당: 생성될 객체에 대해 잘 알고 있어야 하거나 그 객체를 사용해야 하는 객체는 어떤 방식으로든 생성될 객체와 연결(결합)될것 -> 이미 존재하는 객체 사이관계를 이용하여 낮은 결합도 유지

메시지의 어감은 마치 각각의 독립된 객체들 사이에서 주고 받는 데이터 같은 느낌 (Notification) 같은데 예제를 보니 포함하고 있는 객체에 대해서 해당 객체의 퍼블릭 메소드를 호출해 값을 얻는것도 메시지를 처리한다고 한다.

설계를 개선하는 작업은 변경의 이유가 하나 이상인 클래스를 찾는 것부터 시작하는게 좋다.

- 인스턴스 변수가 초기화 되는 시점(응집도가 낮은 클래스는 객체의 속성 중 일부만 초기화 되고 일부는 초기화 되지 않은 상태)
-> 함께 초기화 되는 속성을 기준으로 코드를 분리

- 메서드들이 인스턴스 변수를 사용하는 방식(모든 메서드가 객체의 모슨 속성을 사용한다면 응집도가 높음)
-> 속성 그룹과 해당 그룹에 접근하는 메서드 그룹을 기준으로 코드를 분리

 

역할 = 다형성 = 구체적인 클래스를 알지 못하고 역할에 대해서만 결합되도록 의존성 제한

#다형성 패턴 #변경 보호 패턴(캡슐화 해야할 것은 변경)

결국 다형성 = 캡슐화 인가? 같은걸로 봐도 되나? -> 다형성의 결화로 캡슐화가 이루어 졌다라고 표현하는건가

하나의 클래스가 여러 타입의 행동을 구현하고 있는다면 다형성 패턴에 따라 클래스를 분해하고 책임을 분산 시킨다.
예측 가능한 변경으로 여러 클래스들이 불안정해지면 변경보호패턴에 따라 안정적인 인터페이스 뒤로 변경을 캡슐화 해라

아! 그러니까 class A, class B를 C라는 추상 클래스로 나타내서 타입을 가려주는게 다형성이라면 여기다 인터페이스를 통해서 메서드 시그니처를 쓰는게 캡슐화 인건가

메시지는 결국 클래스 인터페이스 사이에서 처리하도록

구현을 공유하려면 추상 클래스를 이용한다.

갑자기 든 생각은 추상 클래스에 있는 함수를 오버라이드 해서 다형성을 하는거랑 인터페이스 프로토콜을 준수해서 함수를 작성하는것 중에 뭐가 나은가? -> 이말이 결국 상속 vs 합성의 문제가 되는것 같은디?

변경에 대비하는 방법-> 1. 단순하게 설계 2.유연하게 설계
대부분 전자가 좋지만 유사한 변경이 반복적으로 일어나면 2번 방식

완방에 책임 주도 설계를 하기에 어려움 
-> 일단 실행되는 코드 작성후 명확하게 드러나는 책임들을 이동 -> 리팩토링


6장 메시지와 인터페이스

OOP에서 중요도: 클래스 < 객체 < 객체가 수행하는 책임 = 객체가 주고받는 메시지
객체가 수행하는 책임이 객체가 수신할 수 있는 메시지의 기반

객체가 수신하는 메시지 = 객체의 퍼블릭 인터페이스

메시지: operation name + argument + (전송-메시지 수신자)
ex) condition.isSatisfiedBy(screening) 수신자.오퍼레이션명(인자)

메서드: 메시지를 수신했을 떄 실제로 실행되는 함수 또는 프로시저

퍼블릭 인터페이스: 객체가 의사소통을 위해 외부에 공개하는 메시지의 집합

오퍼레이션: 퍼블릭 인터페이스에 포함된 메시지(구현코드가 아닌 시그니처, 추상화)

오퍼레이션의 관점에서 다형성이란 동일한 오퍼레이션 호출에 대해 서로 다른 메서드들이 실행되는것

#메시지 #오퍼레이션 #메서드 #퍼블릭 인터페이스 #시그니처

오퍼레이션 = 퍼블릭 인터페이스..?

좋은 인터페이스 = 최소한의 추상적인 인터페이스 (어떻게 수행하는지가 아닌 무엇을 하는지 표현)

최소한의 인터페이스 = 꼭 필요한 오퍼레이션만을 인터페이스에 포함

  • 디미터 법칙: 내부 구조를 묻는 메시지가 아닌 수신자에게 무언가 시키는 메시지가 좋다.
    • 낯선 자에게 말하지 말라
    • 오직 인접한 이웃하고만 말하라
    • 오직 하나의 도트만 사용하라
    • 클래스 내부 메서드가 아래 조건을 만족하는 인스턴스에게만 메시지를 전송하도록 한다.
      • this 객체
      • 메서드의 메개변수
      • this의 속성
      • this의 속성인 컬렉션의 요소
      • 메서드 내에서 생성된 지역 객체
  • 묻지 말고 시켜라
    • 절차: 정보를 얻은 후 결정, 객체지향: 객체에게 그것을 하도록 시킴
    • 내부 상태를 묻는 오퍼레이션을 인터페이스에 포함하고 있으면 수정해라
    • 내부의 상태를 이용해 결정을 내리는 로직이 외부에 있다면 책임이 외부로 누수된것
  • 의도를 드러내는 인터페이스
  • 명령-쿼리 분리

읽으면서 생각에 남은게 설계는 트레이드오프의 산물이고 5장 내용을 통해서 여러 설계중에서 높은 응집도와 낮은 결합도를 얻을 수 있는 설계를 선택, 또는 6장 내용에서 디미터, 묻지말고 시켜라 등등의 원칙을 따라야 겠다 싶었는데
6-3원칙의 함정에서 바로 초보자와 숙련자를 구분하는 기준에 따라 원칙을 맹목적으로 추종하는 초보자가 되버렸다

객체의 경우 숨기지만 자료구조의 경우 내부 노출

가끔씩은 물어야 한다: 명령-쿼리 법칙

  • 루틴: 어떤 절차를 묶어 호출 가능하도록 이름을 부여한 기능 모듈
    • 프로시져: 정해진 절차에 따라 내부 상태를 변경하는 루틴의 종류 -> 부수효과 발생O, 값 반환X ==> 명령
    • 함수: 절차에 따라 필요한 값을 계산해서 반환하는 루틴의 종류 -> 부수효과 발생X, 값 반환O    ==> 쿼리

 

명령 쿼리 분리 2법칙
1. 객체의 상태 변경 명령은 반환값이 없다.
2. 객체 정보 반환 쿼리는 상태를 변경할 수 없다.

그럼 상태를 변경하고 변경된 결과를 반환하는 함수 하나를 쓰는것 보다
상태를 변경하는 함수를 호출하고 상태를 확인하는 함수를 호출하는게 나은건가

상태를 확인하기 위한 메서드인데 변경을 일으키는건 확실히 나쁜 코드인것 같다.
변경을 일으키는 코드인데 결과 값에 수정된 상태를 반환하던가 completion handler가 있는건 괜찮은것 같다.

728x90

댓글