Skip to content

[6장_양수진] #31

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 7, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -13,5 +13,4 @@ class NoneDiscountMovie(
override fun calculateDiscountAmount(): Money {
return Money.ZERO
}
} {
}
29 changes: 29 additions & 0 deletions src/main/kotlin/yangsooplus/ch06/Event.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package yangsooplus.ch06

import java.time.Duration
import java.time.LocalDateTime

class Event(
private val subject: String,
private var from: LocalDateTime,
private val duration: Duration
) {
fun isSatisfied(schedule: RecurringSchedule): Boolean {
if (from.dayOfWeek != schedule.dayOfWeek
|| from.toLocalTime() != schedule.from.toLocalTime()
||duration != schedule.duration) {
reschedule(schedule) // 여기는 명령
return false // 여기는 쿼리 -> 쿼리 반환하기 전에
}

return true
}

private fun reschedule(schedule: RecurringSchedule) {
from = LocalDateTime.of(from.toLocalDate().plusDays(dayDistance(schedule)), schedule.from.toLocalTime())
}

private fun dayDistance(schedule: RecurringSchedule): Long {
return (schedule.dayOfWeek.value - from.dayOfWeek.value).toLong()
}
}
102 changes: 102 additions & 0 deletions src/main/kotlin/yangsooplus/ch06/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# 6장 메세지와 인터페이스

### 객체지향
- ❌ 클래스를 중심
- ⭕️ 객체가 수행하는 책임에 초점 -> 메세지에 초점

## 01 협력과 메세지

- 클라이언트-서버 모델
- 클라이언트: 메세지를 전송 = 협력을 요청
- 서버: 메세지를 수신
- 협력의 관점에서 메세지
- 1. 수신하는 메세지
- 2. 외부에 전송하는 메세지
- 메서드
- 메세지를 수신했을 때 실제로 실행되는 함수 또는 프로시저
- 인터페이스를 실체화한 클래스 종류에 따라 메서드는 달라질 수 있다.
- 컴파일 시점과 실행 시점의 의미가 달라질 수 있다!

⚠️ 실행 시점에 실행되는 코드는 메세지 수신 객체 타입에 따라 달라지기 때문에 **메세지에 응답할 수 있는 객체가 존재**하고 그 객체가 **적절한 메서드를 선택해서 응답**할 것이라고 믿어야 함

= 서로에 대한 상세 정보 모름. 메세지라는 얇은 끈을 통해 연결 = 두 객체 사이 결합도 낮춤

### 오퍼레이션
- 퍼블릭 인터페이스에 포함된 메세지
- 수행 가능한 어떤 행동에 대한 추상화

### 메세지
- 메세지를 수신했을 때 실제로 실행되는 코드
- 오퍼레이션을 구현한 것

## 02 인터페이스와 설계 품질
- 좋은 인터페이스?
1. 최소한의 인터페이스
2. 추상적인 인터페이스

➡️ 책임 주도 설계를 따르면 충족
- 메시지를 먼저 선택
- 협력과 무관한 오퍼레이션이 스며드는 것 방지 (최소한)
- 메시지가 객체를 선택
- 클라이언트의 의도를 메시지에 표현 (추상적)

## 퍼블릭 인터페이스의 품질을 지키는 원칙

### 디미터 법칙
- 객체의 내부 구조에 강하게 결합되지 않도록 협력 경로를 제한하라
- 내부 구조? 알빠노~
- 메시지를 전송할 때 *특정한 조건을 만족*하는 대상에만 메시지를 전송.
- 수신 객체의 내부 구현을 변경할 때 송신 객체의 구현을 함께 변경하지 않도록.
- shy code (부끄럼타는 코드)
- 불필요한 것 다른 객체에게 노출 ❌
- 물론 다른 객체의 구현에 의존 ❌
- 낮은 결합도 유지
- 캡슐화랑 비슷한 결~
- `object.getValue().getDetailValue()` ⬅️ 디미터 법칙 위반하는 전형적인 예
- 연쇄적으로 메세지 전송 **기차 충돌(train wreck)**
- 내부 구현이 외부로 노출되었을 때의 전형적인 형태.
- detailvalue가 있다는 사실 자체를 알면 안 됨
- 너무 과하게 수용하면 객체의 응집도가 낮아질 수 있다 (무지성)
- 결론) ***내부 구조를 묻지 말고 수신자에게 일을 시켜라.***

### 묻지 말고 시켜라
- 디미터랑 결은 비슷한거 같은데...
- 밀접하게 연관된 정보와 행동을 함께 가지는 객체를 만들 수 있다
- 객체지향의 기본: 함께 변경될 확률이 높은 정보와 행동을 하나의 단위로 통합
- 객체의 정보를 이용하는 행동이 내부에 존재 -> 정보와 행동을 동일한 클래스에 넣게 됨 -> 응집도 up

### 의도를 드러내는 인터페이스
- 메서드 이름짓기
- 메서드가 작업을 어떻게 수행하는지 짓기? ❌(알빠노)
- 메서드가 무엇을 하는지 드러내기 ⭕️
- 동일한 작업을 수행하는 메서드를 하나의 타입 계층으로 묶게 됨!

## 03 원칙의 함정

- 원칙이라고 너무 무지성하게 적용하진 말자. 우리는 지성인이니까.
- 상황에 따라 원칙을 과감히 무시하는것도 설계다~~

### 명령-쿼리 분리 원칙
- 루틴 routine: 어떤 절차를 묶어 호출 가능하도록 이름을 부여한 기능 모듈
ㄴ 프로시저 procedure: 내부의 상태를 변경하는 루틴
- 부수효과 발생, 값 반환 불가
ㄴ 함수 function: 어떤 절차에 따라 필요한 값을 계산해서 반환하는 루틴
- 값 반환, 부수효과 발생 불가

명령: 객체의 상태를 수정하는 오퍼레이션 (프로시저)

쿼리: 객체와 관련된 정보를 반환하는 오퍼레이션 (함수)

➡️ 명령과 쿼리를 분리해야 한다.

🤔 명령과 쿼리가 섞인 오퍼레이션...

(Event와 RecurringSchedule 예시)

➡️ **명령과 쿼리가 섞이니 실행 결과를 예측하기 어렵다!**






12 changes: 12 additions & 0 deletions src/main/kotlin/yangsooplus/ch06/RecurringSchedule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package yangsooplus.ch06

import java.time.DayOfWeek
import java.time.Duration
import java.time.LocalDateTime

class RecurringSchedule(
private val subject: String,
val dayOfWeek: DayOfWeek,
val from: LocalDateTime,
val duration: Duration
)