일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |
Tags
- 메서드를 통한 해결
- 알고리즘
- 객체 생성 사용 분리
- 상속
- OCP
- dip
- 합성
- 설계 재사용
- 다형성
- 유여난 설계
- 유연한 설계
- 객체지향
- 책임주도설계
- 서브 타이핑
- 오브젝트
- 런타임 의존성
- 추상화
- 명령-쿼리 분리
- OOP
- 컴파일 타임 의존성
- 행동 호환성
- Swift#flatMap#map#Monad#함수형 프로그래밍#Optional
- 상속 조합 폭발적 증가
- iSP
- 의존성
- 하향식 접근
- 일관성 있는 협력
- '기존 설계 재사용
- Apple # HIG #iOS15 #iOS14 #Human #Interface #Guidelines #Apple developer # Apple human interface guidelines
- 믹스인
Archives
- Today
- Total
도니의 iOS 프로그래밍 세상
[오브젝트 2회독] 6장 - 메시지와 인터페이스 본문
1. 협력과 메세지
- 객체가 다른 객체와 협력함으로써 독립적인 객체에서 더 큰 책임을 가질 수 있다
- 이때 협력을 위한 매개체가 메시지 이다.
메시지와 메시지 전송
- 메시지 전송자는 클라이언트, 수신자는 서버라고 부르기도 함
- 결국 메시지 전송자는 특정 객체의 함수를 호출하는 객체, 수신자는 특정 객체를 의미
메시지와 메서드
- 메시지를 수신했을 때, 실제 실행되는 함수 또는 프로시저를 메서드 라고 함
- 메시지를 전송시 컴파일 시점과 실행 시점에 따라 메서드가 다를 수 있음
- 이를 통해 유연하고 확장 가능한 코드 작성 가능
2. 인터페이스와 설계 품질
- 좋은 인터페이스는 최소한 + 추상적 인터페이스라는 두가지 조건을 만족시켜야 함
- 이를 위해 책임 주도 설계를 활용한다면, 객체가 필요한 메시지만 가질 수 있게 됨
디미터 법칙
객체 내부 구조에 강한 커플링을 하지 않도록, 협력의 경로를 제한하는 것
- 메시지 수신자의 내부 구조를 묻지 않고 원하는 동작을 수행하도록 요청함(ex. a.b.c()가 아닌, a.requestC())
- 이와 같은 구조는 정보를 알고 있는 객체에게 책임을 주어 더욱 응집도가 높은 코드를 작성할 수 있음
의도를 드러내는 인터페이스
메서드 명명의 두가지 방법
- How(어떻게)를 강조하는 명명
- What(무엇을)을 강조하는 명명법
How를 강조한 예시
public class PeriodCondition {
func isSatisfiedByPeriod(screening: Screening) -> Bool {
// 조건 확인 로직
}
}
public class SequenceCondition {
func isSatisfiedBySequence(screening: Screening) -> Bool {
// 조건 확인 로직
}
}
What을 강조한 예시
public class PeriodCondition {
func isSatisfiedBy(screening: Screening) -> Bool {
// 조건 확인 로직
}
}
public class SequenceCondition {
func isSatisfiedBy(screening: Screening) -> Bool {
// 조건 확인 로직
}
}
- What이 How보다 더욱 좋은 명명법
- How는 내부 구현에 초점을 두는 명명법, 외부에서는 해당 메서드의 구현 방식을 알 필요가 없음
- What에 집중한다면 해당 객체가 지닌 책임을 고려해서 이름을 지을 수 밖에 없음
- 결국 외부에서 객체를 사용할 때 해당 메시지를 통해서 해당 객체에 책임을 명확히 알고 사용할 수 있게 됨
- What을 강조하여 두 Condition class를 하나의 타입 계층으로 묶음으로써, 더욱 유연하게 설계가 가능해짐
- 이를 켄트백은 의도를 드러내는 인터페이스라고 부르며, 더욱 잘하기 위해선 두가지 클래스를 매우 다른 방식으로 구현하여 추상적인 이름으로 한데 묶는 방식을 소개하고 있음
3. 원칙의 함정
- 디미터 법칙등 앞서 말한 설계 원칙들이 훌륭하지만 절대적이지 않음
- 이를통한 설계는 트레이드 오프의 산물이며, 현재 상황에 부적합할 때는 무시하는 것이 필요함
- 원칙보다 중요한 건 해당 원칙들이 언제 유용하고 언제 유용하지 않은 지 판단하는 것이다.
디미터 법칙은 하나의 도트(.)를 강제하는 규칙이 아니다
IntStream.of(1, 15, 20, 3, 9)
.filter(x -> x > 10)
.distinct()
.count();
- 위 코드는 굉장히 많은 도트를 사용하지만 위 함수들은 IntStream 인스턴스를 변환하는 것
- 따라서, 디미터 법칙을 위반하지 않음
- 디미터 법칙의 본질은 결합도와 관련된 것, 결과적으로 하나의 인스턴스와 관련되어 있다면 결합도가 있다고 볼 수 없음
결합도와 응집도의 충돌
1. a.b.c()
2. a.requestC()
- 1번과 2번중 어떤게 더 나은 방식일까?
- 1번은 a클래스 내부 구조를 알고, c함수를 호출한 케이스
- 2번은 결합도를 위해 a클래스 내부 구조를 아는 대신, a클래스에 위임 함수를 추가한 케이스
- 2번에서의 단점은 위임 메서드를 추가함으로써, a와 관련 없는 동작이 추가된 케이스. 이때 응집도가 떨어질 수 있다
- a에서 c라는 위임 메서드를 추가함으로써, 변경했을 때 영향도가 발생함
→ 2번이 반드시 좋지 않은건 아니지만, 이러한 단점이 있을수 있기에 고려해서 설계 해야함
4. 명령-쿼리 분리 원칙
명령
- 객체의 상태를 변경할 수 있지만, 값을 반환할 수 없음
쿼리
- 값을 반환할 수 있지만 객체의 상태를 변경할 수 없다(no side-effect)
- 쿼리 내부에서 값을 변경하거나, 명령이 값을 반환할 경우 side-effect를 동료 개발자는 side-effect를 생각하지 못할 수 있음(HTTP get 함수를 통해서 DB상태가 변경된다고 생각할 수 있는가?)
결론
- 독립적인 객체가 더 큰 작업을 하기 위해선, 협력이 필요하고 이를 위한 매개체는 메시지 이다.
- 메시지 는 컴파일 타임과 실행 시점에 메서드 가 다르기 때문에 더욱 유연한 설계가 가능하다
- 인터페이스의 좋은 품질을 위해선 최소한 + 추상적 인터페이스를 만족시켜야 하고, 이를 위해선 책임 주도 설계가 필요하다
- 디미터 법칙을 통해 객체의 협력 경로를 제한하여 결합도를 낮출 수 있다.
- 이때, 중요한 건 “.”을 제한하는게 아니고, 객체의 결합도를 제한하는 게 중요하다.
- 의도가 드러나는 What을 강조한 명명법을 통해 더욱 유연한 설계가 가능하다.
- How를 강조한 명명법은 내부 구현에 초점을 두어, 유연한 설계를 해친다.
- 앞서 말한 원칙들은 모두 trade-off가 존재하기에, 유용할때와 하지 않을 때를 구분해서 써야한다.(절대 원칙X)
- 조회를 위해 사용하는 메서드는, 객체의 상태를 변경시켜선 안된다.(side-effect를 고려하지 않고 사용하는 함수이기 때문)
'OOP' 카테고리의 다른 글
[오브젝트 2회독] 8장 - 의존성 관리하기 (0) | 2024.11.19 |
---|---|
[오브젝트 2회독] 7장 - 객체 분해 (0) | 2024.11.19 |
[오브젝트 2회독] 5장 - 책임 할당하기 (0) | 2024.11.19 |
[오브젝트 2회독] 4장 - 설계 품질과 트레이드 오프 (0) | 2024.11.19 |
[오브젝트] 12장 - 다형성 (1) | 2024.10.09 |
Comments