[오브젝트] 8장_의존성 관리하기

이번장에서는 충분히 협력적이고 유연한 객체를 만들기 위해, 의존성을 관리하는 방법을 정리한다.

01 의존성 이해하기

변경과 의존성

어떤 객체가 협력을 위해 다른 객체가 필요할 때, 두 객체 사이의 의존성이 존재한다.
아래 코드에서, 어떤 형태로든 DayOfWeek, LocalTime, Screening, DiscountCondition 이 변경되면 PeriodCondition 도 함께 변경될 수 있다.

1
2
3
4
5
6
7
8
9
10
public class PeriodCondition implements DiscountCondition {
private DayOfWeek dayOfWeek;
private LocalTime startTime;
private LocalTime endTime;
...

public boolean isSatisfiedBy(Screening screening){
...
}
}
의존성 전이

PeriodCondition 이 Screening 에 의존하면, PeriodCondition 은 Screening 이 의존하는 대상에 대해서도 의존하게 된다는 것이다.
의존성이 실제로 전이 될지 여부는 변경의 방향과 캡슐화의 정도에 따라 다르다. Screening 이 내부 구현을 효과적으로 캡슐화하면 Screening 에 의존하고 있는 PeriodCondition 까지는 변경이 전파되지 않는다.
의존성의 종류는,

  1. 직접 의존성
    한 요소가 다른 요소에 직접 의존하는 경우. PeriodCondition 이 Screening 에 의존

  2. 간접 의존성
    의존성 전이에 의해 영향이 전파되는 경우

런타임 의존성과 컴파일타임 의존성
Read more

[오브젝트] 7장_객체 분해

하향식 기능 분해 -> 모듈 -> 추상 데이터 타입 -> 클래스

01 프로시저 추상화와 데이터 추상화

  • 데이터 추상화
    • 추상 데이터 타입 : 데이터 중심으로 타입을 추상화
    • 객체지향 : 데이터 중심으로 프로시저를 추상화

02 프로시저 추상화와 기능 분해

메인 함수로서의 시스템

전통적인 기능 분해방법은 하향식 접근법이다. 즉, 최상위 기능을 좀 더 작은 단계의 하위 기능으로 분해해 나가는 방법이다.

급여 관리 시스템

모든 문장이 정제 과정을 거치면서 하나 이상의 좀 더 단순하고 구체적인 문장들의 조합으로 분해된다.

  • 직원의 급여를 계산한다
    • 사용자에게 소득세율을 입력 받는다
      • “세율을 입력하세요 : “ 라는 문장을 화면에 출력한다
    • 직원의 급여를 계산한다
      • 전역 변수에 저장된 직원의 기본접 정보를 얻는다
    • 양식에 맞게 출력한다
급여 관리 시스템 구현
Read more

[오브젝트] 6장_메세지와 인터페이스

이번 장에서는, 유연하고 재사용 가능한 퍼블릭 인터페이스를 만드는데 도움이 되는 설계 원칙과 기법을 익힌다.

01 협력과 메세지

클라이언트-서버 모델
  1. 클라이언트 : 메세지를 전송하는 객체
  2. 서버 : 수신하는 객체

객체는 자신의 희망을 메세지 형태로 전송하고 메세지를 수신한 객체는 요청을 처리하고 응답한다.
객체는 독립적으로 수행할 수 있는 더 큰 책임을 수행하기 위해, 다른 객체와 협력해야 한다.

메세지와 메세지 전송

메세지는 객체들이 협력하기 위해 사용하는 유일한 의사소통 수단이다. 한 객체가 다른 객체에게 도움을 요청하는 것을 메세지 전송이라고 한다.

메세지와 메서드

메세지를 수신했을 때 실제로 수행되는 함수 또는 프로시저를 메서드라고 한다.
메세지 전송자와 메세지 수신자는 서로에 대한 상세한 정보를 알지 못하고 메세지라는 얅고 가는 끈을 통해 연결된다.

퍼블릭 인터페이스와 오퍼레이션
Read more

[오브젝트] 5장_책임 할당하기

데이터 중심 설계로 인한 문제를 해결하기 위한 가장 기본적인 방법은, 책임에 초점을 맞추는 것이다.
이번 장에서는 GRASP 패턴을 살펴본다.
결합도와 응집도를 합리적인 수준으로 유지할 수 있는 원칙이 있다. 객체의 행동에 초점을 맞추는 것이다.
이번 장에서는 책임이 아닌, 상태를 표현하는 데이터 중심의 설계를 보고 객체지향적으로 설계한 구조와 어떤 차이가 있는지 확인한다.
상속, 지연 바인딩도 중요하지만, 구현 측면에 치우쳐져 있기 때문에 객체지향 패러다임의 본질과는 거리가 멀다.

01 책임 주도 설계를 향해

두 가지 원칙이 있다.

  1. 데이터보다 행동을 먼저 결정하라
  2. 협려이라는 문맥안에서 책임을 결정하라

책임을 드러내는 안정적인 인터페이스 뒤로 책임을 수행하기 위해 필요한 상태를 캡슐화함으로써 구현 변경에 대한 파장이 외부로 퍼져나가는 것을 방지한다. 그래서 책임에 초점을 맞추면 안정적인 설계를 할 수 있다.

데이터보다 행동을 먼저 결정하라

데이터는 객체가 책임 수행을 위해 필요한 재료일 뿐이다.
“이 객체가 해야하는 책임은 무엇인가” 를 결정하고, 이 책임을 수행하는데 필요한 데이터는 무엇인가 결정해라.

99 Page

객체의 종류를 저장하는 인스턴스 변수 (movieType) 와 인스턴의 종류에 따라 배타적으로 상용될 인스턴스 변수 (discountAmount, discountPercent) 를 하나의 클래승 안에 포함시키는 방식은 데이터 중심 설계에서 흔히 보인다.
메세지를 수신한 객체는 메세드를 실행해 요청에 응답한다. Screening 이 Movie 에게 처리를 위임하는 이유는 요금을 계산하는데 필요한 기본 요금과 할인 정책을 가장 잘 알고 있는 객체가 Movie 이기 때문이다.
자율적인 객체란 자신의 상태를 직접 관리하고 스스로의 결정에 따라 행동하는 객체이다. 객체의 자율성을 보장하기 위해서는 필요한 정보와 정보에 기반한 행동을 같은 객체 안에 모아두어야 한다.
결과적으로 객체를 자율적으로 만드는 가장 기본적인 방법은 내부 구현을 캡슐화 하는 것이다.
자율적인 객체는 자신에게 할당된 책임을 수행하던 중에 필요한 정보를 알지 못하거나 외부의 도움이 필요한 경우 적절한 객체에게 메세지를 전송해 협력을 요청한다.

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

[오브젝트] 4장_설계 품질과 트레이드오프

훌륭한 설계란, 합리적인 비용 안에서 변경을 수용할 수 있는 구조를 만드는 것이다.
적절한 비용 안에서 쉽게 변경할 수 있는 설계는, 응집도가 높고 서로 느슨하게 결합되어 있다.
결합도와 응집도를 합리적인 수준으로 유지할 수 있는 원칙은, 객체의 행동에 초점을 맞추는 것이다.

캡슐화

상태와 행동을 하나의 객체 안에 모으는 이유는 객체의 내부 구현을 외부로부터 감추기위해서다.
변경될 가능성이 높은 것을 구현, 상대적으로 안정적인 것을 인터페이스라고 한다.
캡슐화란, 변경 가능성이 높은 것을 객체 내부로 숨기는 추상화 기법이다.

응집도

응집도는 모듈에 포함된 내부 요소들이 연관되어 있는 정도이다.
객체 지향 관점에서는, 객체 또는 클래스에 얼마나 관련 높은 책임들을 할당했는지이다.
응집도가 높은 설계에서는, 하나의 요구사항 변경을 반영하기 위해 오직 하나의 모듈만 수정하면 된다.
응집도가 낮은 설계에서는, 하나의 원인에 의해 변경해야 하는 부분이 다수의 모듈에 분산되어 있기 때문에 여러 모듈을 동시 수정해야한다.

결합도

결헙도는 의존성의 정도이다.
다른 모듈에 대해 얼마나 많은 지식을 갖고 있는지를 나타내는 척도이다.
내부 구현을 변경하면 이것이 다른 모듈에 영향을 미치는 경우 두 모듈 사이의 결합도가 높다고 말한다.
반면, 퍼블릭 인터페이스를 수정했을 때만 다른 모듈에 영향을 미치면 결합도가 낮다고 말한다.
그래서 구현이 아닌 인터페이스에 의존하도록 코드를 작성해야 낮은 결합도를 얻을 수 있다.
이것이 인퍼페이스에 대해 프로그래밍 하라 이다.

데이터 중심 설계의 문제점

데이터 중심 설계는 변경에 취약하다. 이유는,

  1. 너무 이른시기에 데이터를 결정하도록 강요한다.
  2. 협력이라는 문맥을 고려하지 않고 객체를 고립시킨 채 오퍼레이션을 결정한다.
Read more

[오브젝트] 3장_역할, 책임, 협력

객체들이 애플리케이션의 기능을 구현하기 위해 수행하는 상호작용을 협력이라고 한다.
객체가 협력에 참여하기 위해 수행하는 로직은 책임이다.
객체들이 협력 안에서는 수행하는 책임들이 모여 객체가 수행하는 역할을 구성한다.

협력

자율적인 객체는 자신에게 할당된 책임을 수행하던 중에 필요한 정보를 알지 못하거나 외부의 도움이 필요한 경우 적절한 객체에게 메세지를 전송해 협력을 요청한다.
메세지 전송은 객체 간 협력을 위해 사용할 수 있는 유일한 수단이다.
메세지를 수신한 객체는 메세드를 실행해 요청에 응답한다.
Screening 이 Movie 에게 처리를 위임하는 이유는 요금을 계산하는데 필요한 기본 요금과 할인 정책을 가장 잘 알고 있는 객체가 Movie 이기 때문이다.

협력이 설계를 위한 문맥을 결정

Movie 객체는 어떤 행동을 수행할 수 있어야할까 ?
Movie 의 행동을 결정하는 것은 영화 예매를 위한 협력이다.
Movie 가 기본 요금인 fee 와 할인 정책인 discountPolicy 라는 인스턴스 변수를 상태의 일부로 포함하고 있는 이유는, 요금 계산이라는 행동을 수행하는데 이 정보들이 필요하기 때문이다.
객체가 참여하는 협력이 객체를 구성하는 행동과 상태를 결정한다.

책임

객체는 협력에 필요한 지식과 방법을 가장 잘 알고 있는 객체에게 도움을 요청한다.

  1. 책임을 할당한다는 것은 메세지의 이름을 결정하는 것과 같다. ex) 예매하라
  2. 메세지를 선택했으면 메세지를 처리할 적절한 객체를 선택해야한다. 영화 예매와 관련된 정보를 가장 많이 알고 있는 객체에게 할당하 것이 바람직다. ex) Screening
  3. 영화 예매를 위해서는 예매 가격을 계산해야한다. Screening 은 예매에 대해서는 정보 전문가지만, 영화 가격 자체에 대해서는 모른다. 그래서 외부의 객체에게 가격 계산을 요청해야한다. 새로운 메세지가 필요하다. ex) 가격을 계산하라
  4. 가격 계산을 위해 정보 전문가가 필요하다. ex) Movie

책임 주도 설계

책임 주도 설계란, 책임을 찾고 책임을 수행할 적절한 객체를 찾아 책임을 할당하는 방식으로 협력을 설계하는 방법이다.
협력은 객체를 설계하기 위한 구체적인 문맥을 제공한다. 협력이 책임을 이끌어내고 책임이 협력에 참여할 객체를 결정한다.
책임을 할당할 때 고려해야할 두 가지 요소

Read more

[오브젝트] 2장_객체지향 프로그래밍

영화 예매 시스템

사용자가 영화 시스템을 이용해서 영화를 예매할 수 있는 애플리케이션을 구현한다.

협력, 객체, 클래스

객치지향 패러다임은, 클래스가 아닌 객체에 초점을 맞춰야한다. 다음 두가지에 집중해야 한다.

  1. 어떤 클래스가 필요한지를 고민하기 전에, 어떤 객체들이 필요한지 고민하라. 클래스는 공통적인 상태와 행동을 공유하는 객체들을 추상화한 것이다.
  2. 객체는 독립적인 존재가 아니라, 기능 구현을 위한 협력 공동체의 일원이다.

도메인의 구조를 따르는 프로그램 구조

도메인이란, 문제를 해결하기 위해 사용자가 프로그램을 사용하는 분야이다.
객체지향 패러다임에서는, 도메인을 구성하는 개념들이 프로그램의 객체와 클래스로 매끄럽게 연결된다.
일반적으로 클래스의 이름은, 대응되는 도메인의 개념의 이름과 동일하거나 유사하게 지어야한다.

클래스 구현하기

클래스를 구현하거나 다른 개발자에 의해 개발된 클래스를 사용할 때 가장 중요한 것은, 클래스의 경계를 구분짓는 것이다.
외부에서는 객체의 속성에 직접 접근할 수 없도록 막고 적절한 public 메서드를 통해서만 내부 상태를 변경할 수 있게해야한다.
경계의 명확성이 객체의 자율성을 보장하기 때문이다. 또, 프로그래머에게 구현의 자유를 제공하기 때문이다.

자율적인 객체

Read more

[오브젝트] 1장_객체, 설계

티켓 판매 애플리케이션

관람객이 초대장이 있으면 초대장을 티켓으로 바꿔서 극장에 입장하고, 없으면 티켓을 구입한 후에 극장에 입장하는 간단한 애플리케이션을 구현한다.

초대장을 구현한다.

1
2
3
4
5
6
import java.time.LocalDateTime;

public class Invitation {

private LocalDateTime when;
}

티켓을 구현한다.

1
2
3
4
5
6
7
8
public class Ticket {

private Long fee;

public Long getFee() {
return fee;
}
}

관람객의 가방을 구현한다.

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
32
33
34
35
public class Bag {

private Long amount;
private Invitation invitation;
private Ticket ticket;

public Bag(Long amount) {
this(null, amount);
}

public Bag(Invitation invitation, Long amount) {
this.invitation = invitation;
this.amount = amount;
}

public boolean hasInvitation() {
return invitation != null;
}

public boolean hasTicket() {
return ticket != null;
}

public void setTicket(Ticket ticket) {
this.ticket = ticket;
}

public void minusAmount(Long amount) {
this.amount -= amount;
}

public void plusAmount(Long amount) {
this.amount += amount;
}
}

관람객을 구현한다.

1
2
3
4
5
6
7
8
9
10
11
12
public class Audience {

private Bag bag;

public Audience(Bag bag) {
this.bag = bag;
}

public Bag getBag() {
return bag;
}
}
Read more