일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 알게되는
- 테스트
- 안드로이드
- 코루틴
- 안드로이드스튜디오
- 안드로이드강좌
- ReactiveProgramming
- 책
- 코틀린
- 글또
- Kotlin
- viewmodel
- mockito
- 병럴프로그래밍
- Rxjava
- 스레드
- android
- Coroutine
- 알고리즘
- 자바
- k8s
- 커스텀상태
- 회고
- Gradle
- theming
- 디자인패턴
- Compose
- 병렬프로그래밍
- g 단위테스트
- kotlin강좌
- Today
- Total
선생님, 개발을 잘하고 싶어요.
[오브젝트] 챕터 6 - 메시지와 인터페이스 본문
메시지와 메서드의 차이를 명확하게 설명하는 챕터였다고 생각합니다.
메시지를 좀더 추상적인 개념으로 받아드리게 되었어요. 이 생각의 변화가 중요하다고 생각한 나름의 이유는 다형성에 있습니다.
결국 코드를 작성하다보면 객체에 함수를 호출하는 형태로 구현되기 마련이죠. 그래서 메시지는 함수인가? 싶은 생각이 들게 됩니다.
하지만 인터페이스나 추상 클래스를 사용하는 경우 코드 상으로는 함수 호출이지만 실제로 실행되는 코드는 런타임에 진짜 객체에 의해서 결정되죠.
그러니까 우리가 생각하는 객체에 함수를 호출하는 행위는 객체에 메시지를 보내는 것입니다.
클라이언트 입장에서 함수의 세부 구현에 대해서 신경쓰지 않는다는 것이죠. 서버로 메시지를 보내면 서버가 타입에 맞게 적절한 구현체를 찾아서 실행합니다. 이 실제 구현체가 메서드를 의미하는 거죠.
메시지와 메서드의 구분은 크게 봤을 때 무의미 해보입니다. (그리고 어느정도는 그렇습니다. 몰라도 되거든요.)
근데 클라이언트 입장에서 함수 호출을 생각할 때, 메시지 차원에서 생각하면 서버 내부 구현에 대한 가설을 세우지 않아야합니다.
또한 좋은 인터페이스를 작성하기 위한 실천 전략을 제시한 게 좋았어요. 직접 본인 코드를 평가할 수 있는 평가 기준을 마련할 수 있습니다.
디미터의 법칙은 아직 좀 더 감을 잡아야겠지만, 묻지 말고 시켜라, 의도를 드러내는 인터페이스는 퍼블릭 인터페이스 네이밍과 시그니처를 정의할 때 좋은 지침이 되는 것 같아요.
명령-쿼리의 구분을 철저히 하자는 것도 좋았습니다. "묻지 말고 시켜라"원칙을 강경하게 지킨다면 사실 쿼리는 필요 없겠죠. 하지만 현실적으로 그건 불가능합니다. "법칙"이 아니라는 거죠. 그렇다면 그렇게 원칙에서 벗어날 때, 트레이드 오프를 어떻게 상쇄시킬 것인가에 대한 얘기였다고 생각합니다.
단순한 getter, setter를 벗어나서 프로시져와 함수의 차이를 생각하고 이를 제 코드 작성에 바로 적용해 볼 수 있던 것이죠.
"아 이건 쿼리구나 그러니까 내부적으로 side effect가 발생한다면 별도의 주석을 남기거나 리팩토링 해야지" 이런 식으로요.
이 책의 장점은 저자의 경험에 있는 것 같습니다. 이 책의 저자는 본인이 쓴 내용을 100% 맹신하는 것에 강한 우려를 표현합니다. 항상 적절한이라는 형용사를 쓰는 이유와 일맥 상통한 부분이죠.
그래서 이번 챕터에서도 제 프로젝트 상황에 취할 수 있는 부분만 최대한 취하려 합니다.
- 앱이 클래스의 집합으로 구성된다고 오해를 한다.
- 클래스는 구현 도구일 뿐
- 도구에 집착하면 경직되고 유연하지 못한 설계에 이를 확률이 높아진다.
- 협력 안에서 객체가 수행하는 책임에 초점을 맞춰야 한다.
- 책임이 객체가 수실할 수 있는 메시지의 기반이 된다.
- 가장 중요한 재료는 클래스가 아니라 객체들이 주고받는 메시지다.
- 클래스 사이의 정적인 관계에서 메시지 사이의 동적인 흐름으로 초점을 전환하자.
<aside> 💡 앱은 클래스로 구현되지만, 메시지로 정의된다
</aside>
<aside> 💡 유연하고 재사용 가능한 퍼블릭 인터페이스를 만드는데 도움이 되는 설계 원칙과 기법을 익히고 적용해야함.
</aside>
- 협력과 메시지
- 클라이언트-서버 모델
- 두 객체 사이의 협력을 구성하는 방법
- 메시지를 매개로 하는 요청과 응답
- 클라이언트: 메시지를 요청하는 객체
- 서버: 메시지에 응답하는 객체
- 협력: 클라이언트가 서버의 서비스를 요청하는 단방향 상호작용
- 서버-클라이언트는 상대적인 개념으로, 하나의 객체는 서버이자 클라이언트일 수 있다.
- 따라서 협력의 관점에서 객체는 두 가지 종류의 메시지 집합으로 구성된다.
- 객체가 수신하는 메시지 집합
- 외부의 객체에게 전송하는 메시지 집합
- 메시지와 메시지 전송
- 메시지 전송, 메시지 패싱: 한 객체가 다른 객체에게 도움 요청
- 메시지 전송자(message sender): 메시지를 전송하는 객체 (caller라고 생각하면 될 듯, 클라이언트)
- 메시지 수신자(message receiver): 메시지를 수신하는 객체 (서버)
- 메시지: 오퍼레이션명 + 인자
- 메시지 전송: 메시지 + 메시지 수신자
- 메시지와 메서드
- 메서드: 메시지를 수신했을 때 실제로 실행되는 함수 또는 프로시저
- 동일한 메시지에 대해서도 객체의 타입에 따라 다른 메서드 실행될 수 있다. → 다형성
- 메시지와 메서드라는 개념은 실행 시점에 연결된다.
- 컴파일 시점과 실행 시점의 의미가 달라질 수 있다.
- 이러한 구분이 sender와 receiver의 결합을 느슨하게 만들 수 있게한다.
- 메서드: 메시지를 수신했을 때 실제로 실행되는 함수 또는 프로시저
- 퍼블릭 인터페이스와 오퍼레이션
- 퍼블릭 인터페이스: 외부에 공개하는 메시지의 집합
- 오퍼레이션: 퍼블릭 인터페이스에 포함된 메시지, 수행 가능한 행동에 대한 추상화, 메시지와 관련된 시그니처
- 시그니처
- 시그니처: 오퍼레이션(or 메서드)의 이름과 파리미터 목록
- 오퍼레이션 → 실행 코드 없이 시그니처
- 메서드 → 시그니처에 구현
- 시그니처: 오퍼레이션(or 메서드)의 이름과 파리미터 목록
- 클라이언트-서버 모델
- </aside>
- </aside>
- 인터페이스와 설계 품질
- 최소한의 인터페이스 + 추상적인 인터페이스 → 좋은 인터페이스
- 최소한: 꼭 필요한 오퍼레이션만 포함
- 추상적인: how가 아니라 what을 표현한다.
- 가장 좋은 방법은 책임 주도 설계 방법을 따르는 것 (저자의 주장)
- 메시지를 먼저 선택하자
- 협력과 무관한 오퍼레이션이 추가되는 걸 막는다. (최소한의 인터페이스)
- 클라이언트의 의도를 메시지에 표현할 수 있다. (추상적인 인터페이스)
- 디미터 법칙 - 묻지 말고 시켜라 - 의도를 드러내는 인터페이스 - 명령-쿼리 분리
- 디미터 법칙
- 객체의 내부 구조에 강하게 결합되지 않도록 협력 경로를 제한하라
- 특정한 조건을 만족하는 객체에게만 메시지를 보내야한다.
- this 객체
- 메서드의 매개변수
- this의 속성
- this의 속성인 컬렉션의 요소
- 메서드 내에서 생성된 지역 객체
- sender가 receiver의 내부 정보를 자세히 알게되면 문제다.
- receiver에게 시키는 메시지가 좋은 메시지다.
- </aside>
- 묻지 말고 시켜라 (Tell, Don’t Ask)
- 객체의 외부에서 해당 객체의 상태를 기반으로 결정내리는 것은 캡슐화 위반?
- 명령-쿼리 패턴을 보면... 상태를 쿼리하고 그 기반으로 명령을 내리는 샘플이 있는데?
- 내부 상태를 묻는 오퍼레이션이 있다면, 더 나은 방법은 없는지 고민하자.
- 객체가 어떻게 작업을 수행하는지 노출해서는 안 된다.
- 객체의 외부에서 해당 객체의 상태를 기반으로 결정내리는 것은 캡슐화 위반?
- 의도를 드러내는 인터페이스
- 어떻게가 아니라 무엇을 하는지 드러내자.
- 어떻게 수행하는지 드러내는 이름은 메서드의 내부 구현을 설명하는 이름.
- 협력과 관련된 의도만을 표현해야 한다..
- 수행 방법에 관해서는 언급하지 말고 결과와 목적만을 포함하도록 클래스와 오퍼레이션의 이름을 부여하자.
- how를 표현하는 네이밍은 외부 인터페이스로 적합하지 않다.
- 함께 모으기
- 디미터 법칙 → 갭슐화를 위반하는 메시지가 인터페이스에 포함되지 않도록 제한
- 묻지 말고 시켜라 → 디미터 법칙을 준수하는 협력을 만들기 위한 스타일 제시
- 의도를 드러내는 인터페이스 → 퍼블릭 인터페이스 이름 지침 제공 (코드의 목적을 명확하게 커뮤니케이션)
- 원칙의 함정</aside></aside>
- 객체 내부 구현에 대한 어떤 정보도 외부로 노출하지 않는다면, 그건 디미터 법칙을 준수한 것
- 디미터 법칙과 묻지 말고 시켜라 원칙을 무작정 따르면 → 응집도가 낮은 객체가 양산될 것
- 객체에게 시키는 것이 항상 가능한 것은 아니다. 가끔씩은 물어야 한다.
- <aside> 💡 정말 중요한 건, 언제 원칙이 유용하고 언제 유용하지 않은지 판단할 능력을 기르는 것
- <aside> 💡 소프트웨어 설계에 법칙은 존재하지 않는다. 원칙에는 예외가 넘쳐난다.
- </aside>
- 명령-쿼리 분리 원칙
- 가끔씩을 필요에 따라 물어야한다 (쿼리 해야한다!) → 그렇다면 기준을 가지고, 쿼리와 명령을 구분하자.
- 프로시저: 부수효과 발생시킬 수 있지만 값을 반환할 수 없다. (Unit function)
- 함수: 값을 반환하지만 부수효과가 없다.
- 명령: 객체의 상태를 수정, 반환값 없음
- 쿼리: 객체와 관련된 정보를 반환, 상태 변경할 수 없음
- 쿼리인 줄 알았는데, 상태를 변경할 경우 디버그하기 어렵다.
- 참조 투명성
- 불변성 → 부수효과 발생 방지 → 참조 투명성 만족
- 부수효과 있는 명령, 부수효과 없는 쿼리를 명확하게 분리하면
- 쿼리에 대해서는 참조 투명성의 혜택을 누릴 수 있다.
- 책임에 초점을 맞춰라
- 구현 이전에 협력에 초점을 맞추고 협력 방식을 단순하고 유연하게 만들면 된다.
'일상 > 책 리뷰' 카테고리의 다른 글
[오브젝트] 챕터 8 - 의존성 관리하기 (0) | 2022.03.14 |
---|---|
[오브젝트] 챕터 7 - 객체 분해 (0) | 2022.03.05 |
[오브젝트] 챕터 5 - 책임 할당하기 (0) | 2022.03.02 |
[오브젝트] 챕터 4 - 설계 품질과 트레이드오프 (0) | 2022.03.01 |
[오브젝트] 챕터 3 - 역할, 책임, 협력 (0) | 2022.03.01 |