5장. 책임과 메시지
5장을 한 문장으로 정리하면 ‘객체의 자율성이 협력의 품질을 결정한다’
객체가 자율적으로 책임을 수행해야 협력이 단순해지고, 응집도가 올라가고, 캡슐화가 잘 된다. 결론적으로 설계의 확장성과 유연성, 재사용성이 향상된다.
‘객체가 자율적이다’
어떤 요청을 받았을 때, 그 책임을 어떻게 수행할 건지에 대해서는 객체 본인이 판단한다는 의미.
객체의 책임이 너무 구체적이면 객체의 자율성이 침해되고, 객체의 책임이 너무 추상적이면 객체가 해야 할 행동이 명확하지 않아진다.
1. 객체가 너무 구체적인 책임을 지게 되어 자율성이 침해되는 경우
Notification 객체에게 부여된 책임
- type을 판단하라
- 판단된 type에 따라서 알림을 전송하라
문제점?
- 호출부의 문자열 타입에 오타가 있는 경우 런타임 에러 위험 → 타입 안정성
- 전송 로직이 수정되거나 전송 타입이 추가될 경우 Notification 객체 내부 로직 수정 필요 → 확장성, 응집도
class Notification {
public void send(String type, String message) {
if ("EMAIL".equals(type)) {
System.out.println("[이메일 발송] " + message);
} else if ("SMS".equals(type)) {
System.out.println("[문자 발송] " + message);
}
}
}
public class TooSpecificExample {
public static void main(String[] args) {
Notification notification = new Notification();
// 호출자가 방식까지 지정해야 함 → 자율성 부족
notification.send("EMAIL", "회원가입을 환영합니다!");
notification.send("SMS", "비밀번호를 재설정하세요.");
}
}
2. 객체가 자율성을 가지고 스스로 판단하는 경우
Notification 객체는 ‘알림을 보내라’는 책임 한 가지만 가짐
실제 발송 책임은 Sender라는 객체에게 맡김
Notification은 Sender에게 요청해야 하므로 내부에 Sender 인터페이스를 필드로 가짐
- 필드로 인터페이스를 갖는 이유
- 다형성을 활용하기 위해서
- Notification이 Sender의 세부 구현을 알게 되면 새로운 전송 방법이 생겼을 때 Notification 클래스의 코드도 함께 변경됨
interface Sender {
void send(String message);
}
class EmailSender implements Sender {
public void send(String message) {
System.out.println("[이메일 발송] " + message);
}
}
class SmsSender implements Sender {
public void send(String message) {
System.out.println("[문자 발송] " + message);
}
}
class Notification {
private Sender sender;
// 어떤 Sender를 쓸지 생성자에서 결정 → 자율성 확보
public Notification(Sender sender) {
this.sender = sender;
}
public void notify(String message) {
sender.send(message); // 내부에서 어떻게 보낼지 스스로 결정
}
}
public class AutonomousExample {
public static void main(String[] args) {
Notification emailNoti = new Notification(new EmailSender());
emailNoti.notify("회원가입을 환영합니다!");
Notification smsNoti = new Notification(new SmsSender());
smsNoti.notify("비밀번호를 재설정하세요.");
}
}
객체가 자율적이려면?
묻지 말고 시켜라 (Tell, Don’t Ask)
데이터를 꺼내서 외부에서 처리하지 말고, 그 객체에게 직접 시켜서 책임을 맡기라는 뜻이다.
객체가 본인이 가지고 있는 데이터를 외부에 노출하지 않고 요청 받으면 스스로 처리하는 것 즉, 객체가 스스로 책임을 수행할 수 있는 것도 객체의 자율성이다.
메시지를 믿어라
메시지를 먼저 결정하고 그 메시지를 요청할 객체를 선택하는 방식을 따르면 객체의 자율성은 자연히 따라온다.
발신자는 수신자를 몰라도 되며, 자신이 전송한 메시지를 잘 처리할 것이라고 믿고 메시지를 전송할 수밖에 없다.
좋지 않은 예시
- 객체의 내부 데이터와 계산 로직이 외부에 노출됨
- 발신자가 수신자의 데이터와 로직을 모두 알게 됨 → 객체의 자율성 x
class Order {
private List<Integer> itemPrices = new ArrayList<>();
public void addItem(int price) {
itemPrices.add(price);
}
// 단순히 데이터 접근 메서드
public List<Integer> getItemPrices() {
return itemPrices;
}
}
public class AskExample {
public static void main(String[] args) {
Order order = new Order();
order.addItem(3000);
order.addItem(5000);
// 외부에서 데이터를 꺼내서 직접 합계 계산
int total = 0;
for (int price : order.getItemPrices()) {
total += price;
}
System.out.println("총 가격: " + total);
}
}
좋은 예시
- 메시지를 믿고 전송하는 경우
- 발신자는 수신자를 알 필요 없이 메시지를 잘 수행할 것을 믿고 메시지만 보냄
- 수신자는 메시지를 받으면 데이터와 로직을 사용해 자율적으로 처리함
class Order {
private List<Integer> itemPrices = new ArrayList<>();
public void addItem(int price) {
itemPrices.add(price);
}
// 이제 외부는 총합 계산을 직접 하지 않고 Order에게 시킴
public int calculateTotalPrice() {
return itemPrices.stream().mapToInt(Integer::intValue).sum();
}
}
public class TellExample {
public static void main(String[] args) {
Order order = new Order();
order.addItem(3000);
order.addItem(5000);
// 외부는 단순히 "총 가격 계산해줘"라고만 시킴
System.out.println("총 가격: " + order.calculateTotalPrice());
}
}'스터디' 카테고리의 다른 글
| [객체지향의 사실과 오해] 7장 정리 (0) | 2025.08.31 |
|---|---|
| [객체지향의 사실과 오해] 3장, 4장 정리 (3) | 2025.08.16 |
| [객체지향의 사실과 오해] 1장, 2장 정리 (4) | 2025.08.09 |