결국엔 프로그래밍
[OBJECT] 오브젝트 Chapter 2 - 객체지향 프로그래밍 본문
예제에 관해
2장은 예제로 영화 예매 시스템을 다루고 있다.
예제의 특징 중 하나를 짚고 넘어가자면, '영화'와 '상영'을 구분짓고 있다.
'영화'란 영화 그 자체를 말한다. 기본적인 영화의 제목, 상영 기간 등의 정보를 갖고 있다.
'상영'은 우리가 흔히 예매하는 대상을 의미한다. '영화'와 상영 시간 등의 정보를 갖고 있다.
각 '영화'는 하나의 '할인 정책'을 갖는다. 그리고 '할인 조건'은 복수될 수 있다.
객체지향 프로그래밍을 할 때
1) 어떤 객체들이 필요한지 고민하라.
객체를 중심에 두는 접근 방법은 설계를 단순하고 깔끔하게 만든다.
2) 각 객체를 협력하는 공동체의 일원으로 보라.
객체를 협력하는 공동체의 일원으로 보는 것은 설계를 유연하고 확장 가능하게 만든다.
도메인의 구조를 따르는 프로그램 구조
객체지향 패러다임이 강력한 이유는 요구사항을 분석하는 초기 단계부터 프로그램을 구현하는 마지막 단계까지 객체라는 동일한 추상화 기법을 사용할 수 있기 때문이다.
도메인의 개념과 관계를 반영하도록 프로그램을 구조화해야하기 때문에 클래스의 구조는 도메인의 구조와 유사한 형태를 띠어야 한다.
클래스 구현
클래스는 내부와 외부로 구분되며 훌륭한 클래스를 설계하는 핵심은 어떤 것을 외부에 공개하고 어떤 것을 내부로 감출지를 결정하는 것이다.
이렇게 하면, 경계의 명확성이 객체의 자율성을 보장하고 프로그래머에게 구현의 자유를 제공한다.
많은 객체지향 프로그램에서 '접근 제어'를 위한 '접근 수정자'를 제공한다.

자율적인 객체
객체 내부에 대한 접근을 통제하는 이유는 객체를 자율적인 존재로 만들기 위해서다.
캡슐화와 접근 제어는 객체를 두 부분으로 나눈다. 하나는 외부에서 접근 가능한 부분으로 '퍼블릭 인터페이스'라고 부른다. 그리고 외부에서 접근이 불가하고 오직 내부에서만 접근이 가능한 부분을 구현(implementation)이라 부른다.
프로그래머의 자유
클라이언트 프로그래머가 숨겨 놓은 부분에 마음대로 접근할 수 없도록 방지함으로써 클라이언트 프로그래머에 대한 영향을 걱정하지 않고도 내부 구현을 마음대로 변경할 수 있는 것을 '구현 은닉(implementation hiding)이라 부른다.
객체의 외부와 내부를 구분하면 클라이언트 프로그래머가 알아야 할 지식의 양이 줄어들고 클래스 작성자가 자유롭게 구현을 변경할 수 있는 폭이 넓어진다.
설계는 변경을 관리하기 위해서 필요한데, '접근 제어'를 통해 변경될 가능성이 있는 세부적인 구현 내용을 private 영역 안에 감춤으로써 변경으로 인한 혼란을 최소화 할 수 있다.
협력하는 객체
객체지향의 장점은 객체를 이용해 도메인의 의미를 풍부하게 표현할 수 있다는 것이다.
개념을 명시적으로 표현한다는 것은 전체적인 설계의 명확성과 유연성을 높이는 첫 걸음이다.
객체의 내부 상태는 외부에서 접근할 수 없도록 감춘다. 대신 외부에 퍼블릭 인터페이스를 공개해서 이를 통해 내부에 접근할 수 있도록 해준다.
다른 객체와 상호작용하기 위해선 '메세지'를 전송해야한다. 그리고 '메세지'를 수신한 객체는 적절한 '메소드'를 선택해 실행한다.
02장 02절 까지
할인 정책과 할인 조건
할인 정책과 할인 조건에 대한 설계를 하면서 '추상 클래스'의 개념을 활용한다.
먼저, 할인 정책인 DiscountPolicy는 두가지로 나눠진다. AmountDiscountPolicy(값할인)와 PercentDiscountPolicy(비율할인)다.
여기서 '값할인'과 '비율 할인'은 같은 할인 정책의 일종이라 중복되는 부분이 존재한다. 이러한 부분을 부모 클래스인 DiscountPolicy가 추상 클래스로 만들고 두 개의 자식 클래스는 이를 Overriding(오버라이딩)한다.
이처럼 부모 클래스에서 기본적인 알고리즘의 흐름을 구현하고 중간에 필요한 처리를 자식 클래스에게 위임하는 디자인 패턴을 "TEMPLATE METHOD 패턴" 이라 부른다.
오버라이딩과 오버로딩
자바를 공부할 때 마다 마주하는 헷갈리는 개념이었다.
"오버라이딩"은 부모 클래스에서 정의된 이름, 같은 파라미터 목록을 가진 메서드를 자식 클래스에서 재정의 한다. 이 경우 외부에서는 부모 클래스의 메서드가 보이지 않는다.
"오버로딩"은 메서드의 이름은 같지만 제공되는 파라미터의 목록이 다르다. 원래의 매서드를 가리지 않기 때문에 공존이 가능하다.
public class Money{
// Money 타입의 파라미터
public Money plus(Money amount){
return new Money(this.amount.add(amount.amount));
}
// long 타입의 파라미터
public Money plus(long amount){
return new Money(this.amount.add(BigDecimal.valueOf(amount)));
}
}
이 경우 메서드는 공존하며 외부에서 두 개의 메서드를 모두 호출할 수 있다. 즉 공존이 가능하다. 이를 오버로딩이라 부른다.
올바른 상태를 가진 객체
public abstract class DiscountPolicy{
// DiscountPolicy의 생성자가 여러개의 DiscountCondition 인스턴스를 허용
public DiscountPolicy(DiscountCondition ... conditions){
this.conditions = Arrays.asList(conditions);
}
}
이처럼 파라미터의 목록을 이용해 초기화에 필요한 정보를 전달하도록 강제하면 올바른 상태를 가진 객체의 생성을 보장할 수 있다.
02장 03절 할인 요금 구하기까지(p.55)
04. 상속과 다형성
컴파일 시간 의존성과 실행 시간 의존성

Movie는 DiscountPolicy와 연관 관계를 갖고, AmountDiscountPolicy와 PercentDiscountPolicy는 DiscountPolicy를 상속받음
이처럼 1) 어떤 클래스가 다른 클래스에 접근할 수 있도록 경로를 갖거나 2) 해당 클래스의 객체의 메서드를 호출할 경우,
→ 두 클래스 사이의 의존성이 존재한다.
여기서 Movie 클래스와 AmountDiscountPolicy 혹은 PercentDiscountPolicy의 연관 관계는 코드상으로 확인하기 어렵다.
오직 추상 클래스인 DiscountPolicy에만 의존하고 있다.
이러한 의존성은 Movie의 인스턴스 생성에서 확인된다.
public class Movie{
// Movie 생성자 메소드
public Movie(String title, Duration runningTime, Money fee,
DiscountPolicy discountPolicy){
...
this.discountPolicy = discountPolicy;
}
}
Movie의 생성자는 DiscountPolicy 타입의 객체를 인자로 받는다.
Movie avatar = new Movie("아바타",
Duration.ofMinutes(120),
Money.wons(10000),
new AmountDiscountPolicy(Money.wons(800),
...)
);
만약 금액 할인 정책을 영화에 적용하고 싶다면, Movie의 인스턴스를 생성할 때 인자로 DiscountPolicy 자리에 AmounDiscountPolicy를 전달하면 된다. (비율 할인 정책을 적용하고 싶다면, 같은 방식으로 PercentDiscountPolicy를 전달하면 된다.)
이러한 객체지향 설계는 "코드의 의존성과 실행 시점의 의존성이 서로 다를 수 있다." (클래스 사이의 의존성과 객체 사이의 의존성이 다를 수 있다.)
- 다를수록 코드를 이해하기 어려워진다.
- 코드는 더 유연해지고 확장 가능해진다.
→ 트레이드 오프 (유연성과 가독성 사이에서의 고민이 필요함)
차이에 의한 프로그래밍
기존의 클래스와 매우 비슷한 클래스를 만들어야하는 경우
해당 클래스를 재사용하는 방법을 상속이라 한다.
상속
- 클래스 사이의 관계를 설정해 기존 클래스가 가진 모든 속성과 행동을 새로운 클래스에 포함할 수 있다.
- 오버라이딩을 통해 부모 클래스의 구현을 공유하고 자식 클래스의 새로운 행동을 추가할 수 있다.
- 즉, 부모 클래스와 다른 부분만 추가해서 새로운 클래스를 쉽고 빠르게 만든다.(차이에 의한 프로그래밍)
→ 코드의 중복을 제거하고, 여러 클래스에서 동일한 코드를 공유할 수 있게 된다.
클래스의 관계는 상대적이다. 어떤 클래스를 기준으로 하느냐에 따라 상속 관계에 참여하는 클래스의 역할이 달라진다.
p.61 까지
'객체지향프로그래밍 > [책] Object 오브젝트' 카테고리의 다른 글
| [OBJECT] 오브젝트 Chapter 5 - 책임 할당하기 (0) | 2022.01.23 |
|---|---|
| [OBJECT] 오브젝트 Chapter 4 - 설계 품질과 트레이드오프 (0) | 2022.01.16 |
| [OBJECT] 오브젝트 Chapter 3 - 역할, 책임, 협력 (0) | 2022.01.09 |
| [OBJECT] 오브젝트 Chapter 2 - 객체지향 프로그래밍 (part2) (0) | 2022.01.07 |
| [OBJECT] 오브젝트 Chapter 1 - 객체, 설계 (0) | 2022.01.02 |