옵저버(Observer) 패턴이란?
- 옵저버 패턴은 주제(Subject)와 관찰자(Observer)가 공통 인터페이스를 통해 서로 통신할 수 있도록 함으로써 작동한다.
- 주제는 관찰자 목록을 유지하고 관심 있는 이벤트나 데이터가 발생하면 인터페이스에서 메소드를 호출하여 각 관찰자에게 알린다.
옵저버 패턴의 장점
유연성
- 옵저버의 주제(Subject)에 영향을 주지 않고 옵저버를 쉽게 추가하고 제거할 수 있도록 함으로써 시스템 설계에 유연성을 제공한다.
느슨한 결합
- 옵저버 패턴의 주제(Subject)와 관찰자(Observer)가 서로의 구현 세부 사항을 알 필요없이 공통 인터페이스를 통해 통신하여 개체간의 느슨한 결합을 촉진한다.
- 따라서, 코드의 다른 부분에 영향을 주지 않고 시스템을 수정하거나 확장하는데 용이하다.
옵저버 패턴의 단점
성능
- 변경 사항이 있을 때 주제(Subject)가 각 관찰자(Observer)에게 변경 사항을 알려야 함으로 시스템 속도가 느려질 수 있다.
- 관찰자가 많은 시스템이라면 특히 문제가 될 수 있다.
동기화 및 스레드 안전성
- 멀티 스레드 시스템에서는 관찰자(Observer)가 순서가 맞지 않거나 일관성 없는 상태로 알림을 받을 수 있기 때문에 시스템에서 예기지 않은 동작이나 오류가 발생할 수 있다.
- 관찰자(Observer)는 스레드로부터 안전하지 않을 수 있기 때문에 경쟁 조건(Race Condition)이나 동시성(Concurrency) 문제가 발생할 수 있다.
구현 예제
다음은 게임의 파티 시스템을 예로 들어 설명한 옵저버 패턴의 구현 예제이다.
먼저 옵저버 패턴의 클래스를 정의한다.
// Subject class (관찰자를 등록하고 알림을 보내는 주제 인터페이스)
public interface Party {
void registerObserver(PartyMember member); // 옵저버 등록
void removeObserver(PartyMember member); // 옵저버 제거
void notifyObservers(); // 옵저버에 알림 전송
}
// Concrete subject class (주제 인터페이스를 구현한 클래스)
public class HuntingParty implements Party {
private final List<PartyMember> observers = new ArrayList<>(); // 옵저버 목록
private boolean changed = false; // 변경사항이 업데이트되고 옵저버에 알릴 준비가 되었는지 true/false
private String name = "HuntingParty"; // 파티 이름
@Override
public void registerObserver(PartyMember member) { // 옵저버 등록
if (!observers.contains(member) {
observers.add(member);
}
}
@Override
public void removeObserver(PartyMember member) { // 옵저버 제거
observers.remove(member);
}
@Override
public void notifyObservers() { // 옵저버에 알림 전송
if (changed) {
observers.forEach(observer -> observer.update(this));
changed = false;
}
}
public void elementChanged() {
changed = true;
notifyObservers();
}
public void setName(String name) { // 파티 이름 변경
this.name = name;
elementChanged(); // 파티 이름이 변경되었다면 옵저버에게 알림 전송
}
public String getName() {
return name;
}
}
// Observer class (관찰자 인터페이스)
public interface PartyMember {
void update(Party party); // 주제(Subject) 클래스의 변경된 정보를 넘겨받는다.
}
// Concrete observer class (관찰자 인터페이스를 구현한 클래스)
public class Player implements PartyMember {
private Party party; // 주제(Subject)
public Player(Party party) {
this.party = party;
party.registerObserver(this); // 주제에 옵저버 등록
}
@Override
public void update(Party party) { // 주제에서 notifyObservers()를 호출하면 실행된다.
if (party instanceof HuntingParty huntingParty) {
System.out.println("파티 이름: " + huntingParty.getName());
}
}
}
다음은 파티(주제)를 생성하고 파티에 참가/탈퇴(관찰자 등록/제거) 하는 예제이다.
public static void main(String[] args) {
Party party = new HuntingParty(); // 주제(Subject) 객체 생성
Player player = new Player(party); // 관찰자(Observer) 객체 생성 및 주제(Subject)에 참가
party.removeObserver(player); // 주제(Subject)에서 관찰자(Observer)제거
}
다음은 두명의 플레이어(관찰자)가 파티(주제)에 참가하고 이름을 변경(알림)하는 예제이다.
public static void main(String[] args) {
Party party = new HuntingParty(); // 주제(Subject) 객체 생성
Player player1 = new Player(party); // 관찰자(Observer) 객체 생성 및 주제(Subject)에 참가
Player player2 = new Player(party); // 관찰자(Observer) 객체 생성 및 주제(Subject)에 참가
party.setName("HuntingParty (2/4)"); // 파티의 이름을 변경하고 관찰자(Observer)에게 알림 전송
}
'공부합시다 > DesignPattern' 카테고리의 다른 글
[Design Pattern] 싱글톤(Singleton) 패턴 (0) | 2023.04.09 |
---|---|
[Design Pattern] 데코레이터(Decorator) 패턴 (0) | 2023.04.05 |
[Design Pattern] 스트래티지(Strategy) 패턴 (0) | 2023.04.02 |