옵저버 패턴
- 객체의 상태 변화를 감지하고 그 변화를 통지받는 다른 객체들에게 자동으로 전달하는 디자인 패턴
- 주로 이벤트 기반 시스템이나 데이터 변경 통지를 구현할 때 유용
- publish-subscribe 패턴을 구현할 수 있음

주요 구성 요소
1. Subject
- 상태 변화를 감지하는 객체로, 옵저버 객체들을 관리하고, 상태 변화가 있을 때 모든 옵저버들에게 알리는 역할
2. Observer
- Subject 객체의 상태 변화를 통지받는 객체로, Subject 객체에 등록되어 있으며, Subject 객체의 상태가 변할 때 업데이트 메서드를 호출하는 역할
옵저버 패턴 구현 예시
- 옵저버 패턴을 사용하여 크리켓 데이터를 관리하고 표시하는 시스템을 구현하는 예시
- 용어 설명
- wicket은 아웃된 타자의 수
- overs는 현재 경기가 진행된 오버 수
1. Observer 인터페이스
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public interface Observer { | |
void update(int runs, int wickets, float overs); | |
} |
2. Subject 인터페이스
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public interface Subject { | |
void registerObserver(Observer o); | |
void unregisterObserver(Observer o); | |
void notifyObservers(); | |
} |
3. ConcreteSubject
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class CricketData implements Subject { | |
private int runs; | |
private int wickets; | |
private float overs; | |
private List<Observer> observers; | |
public CricketData() { | |
observers = new ArrayList<>(); | |
} | |
@Override | |
public void registerObserver(Observer o) { | |
observers.add(o); | |
} | |
@Override | |
public void unregisterObserver(Observer o) { | |
observers.remove(o); | |
} | |
@Override | |
public void notifyObservers() { | |
for (Observer observer : observers) { | |
observer.update(runs, wickets, overs); | |
} | |
} | |
private int getLatestRuns() { | |
return 90; | |
} | |
private int getLatestWickets() { | |
return 2; | |
} | |
private float getLatestOvers() { | |
return 10.2f; | |
} | |
public void dataChanged() { | |
runs = getLatestRuns(); | |
wickets = getLatestWickets(); | |
overs = getLatestOvers(); | |
notifyObservers(); | |
} | |
} |
4. ConcreteObject
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class AverageScoreDisplay implements Observer { | |
private float runRate; | |
private int predictedScore; | |
@Override | |
public void update(int runs, int wickets, float overs) { | |
this.runRate = (float)runs / overs; | |
this.predictedScore = (int)(runRate * 50); | |
display(); | |
} | |
public void display() { | |
System.out.println("평균 점수 및 예상 점수 출력"); | |
System.out.println("Run Rate: " + runRate); | |
System.out.println("예상 점수: " + predictedScore); | |
} | |
} |
5. 클라이언트 코드
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Client { | |
public static void main(String[] args) { | |
CricketData cricketData = new CricketData(); | |
CurrentScore currentScore = new CurrentScore(); | |
AverageScoreDisplay averageScoreDisplay = new AverageScoreDisplay(); | |
cricketData.registerObserver(currentScore); | |
cricketData.registerObserver(averageScoreDisplay); | |
cricketData.dataChanged(); | |
} | |
} |

부연 설명
- ConcreteSubject인 CricketData는 데이터 변경 시 옵저버인 CurrentScore와 AverageScoreDisplay에 알림
- 이를 통해 옵저버들은 주제의 상태 변화를 실시간으로 반영하여 데이터를 표시
옵저버 패턴 장단점
장점
- 상태를 변경하는 객체인 publisher와 변경을 감지하는 객체(subscriber)의 관계를 느슨하게 유지 가능 (loosely coupled)
- Subject의 상태 변경을 주기적으로 조회하지 않고 자동으로 감지 가능
- 런타임에 옵저버를 추가하거나 제거할 수 있음
단점
- 옵저버가 많아질수록 복잡도 증가
- 다수의 옵저버 객체를 등록한 후 해제하는 작업을 거치지 않을 경우 메모리 누수 발생할 수 있음
실무에서 쓰이는 옵저버 패턴
1. 자바 8 버전까지 지원하는 Observable과 Observer
- 자바에서 Observable과 Observer는 옵저버 패턴을 구현하기 위한 클래스로, java.util 패키지에 포함되어 있음
- Observable은 Subject 역할을 하고, Observer는 옵저버 역할
- 자바 9 버전부터는 Deprecated
2. Flow API
- 비동기 스트림 처리와 반응형 프로그래밍을 지원하기 위한 API
- 옵저버 패턴과 유사한 개념을 사용하여 데이터의 흐름을 관리하며, Subject와 Observer 간의 관계를 설정
Flow API 주요 구성 요소
- Flow.Publisher<T>: Subject 역할을 하며, Flow.Subscriber에게 데이터를 발행
- Flow.Subscriber<T>: Observer 역할을 하며, Flow.Publisher로부터 데이터를 수신
- Flow.Processor<T, R>: Flow.Publisher와 Flow.Subscriber를 모두 구현하여 데이터의 중간 처리 역할
- Flow.Subscription: Flow.Publisher와 Flow.Subscriber 간의 연결을 관리하며, 데이터의 요청과 취소를 처리
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class NewsPublisher extends SubmissionPublisher<String> { | |
public void publishNews(String news) { | |
System.out.println("뉴스 발행: " + news); | |
submit(news); | |
} | |
} | |
public class NewsSubscriber implements Flow.Subscriber<String> { | |
private Flow.Subscription subscription; | |
@Override | |
public void onSubscribe(Flow.Subscription subscription) { | |
this.subscription = subscription; | |
subscription.request(1); // 처음 요청 시 1개의 아이템 요청 | |
} | |
@Override | |
public void onNext(String item) { | |
System.out.println("뉴스 수신: " + item); | |
subscription.request(1); // 다음 아이템 요청 | |
} | |
@Override | |
public void onError(Throwable throwable) { | |
throwable.printStackTrace(); | |
} | |
@Override | |
public void onComplete() { | |
System.out.println("모든 뉴스 수신 완료"); | |
} | |
} | |
public class Client { | |
public static void main(String[] args) { | |
NewsPublisher newsPublisher = new NewsPublisher(); | |
NewsSubscriber newsSubscriber = new NewsSubscriber(); | |
newsPublisher.subscribe(newsSubscriber); | |
newsPublisher.publishNews("뉴스 1!"); | |
newsPublisher.publishNews("뉴스 2"); | |
newsPublisher.close(); | |
} | |
} |

3. Spring 프레임워크 ApplicationContext와 ApplicationEvent
- ApplicationContext: 스프링의 중앙 인터페이스로, 애플리케이션의 구성 정보를 제공하고 빈을 관리
- ApplicationContext는 이벤트를 발행하고, 이벤트 리스너를 등록하여 이벤트를 처리하는 기능 제공
- ApplicationEvent: 스프링에서 발생할 수 있는 이벤트의 기본 클래스
- 모든 커스텀 이벤트는 헤당 클래스를 상속받아 정의
- 정리하면 스프링 이벤트 모델에서는 ApplicationContext가 Subject 역할을 하고, ApplicationEvent가 이벤트, 그리고 이벤트 리스너가 Observer 역할
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Component | |
public class EventPublisher { | |
@Autowired | |
private ApplicationEventPublisher applicationEventPublisher; | |
public void publishEvent(String message) { | |
CustomEvent customEvent = new CustomEvent(this, message); | |
applicationEventPublisher.publishEvent(customEvent); | |
} | |
} | |
public class CustomEvent extends ApplicationEvent { | |
private String message; | |
public CustomEvent(Object source, String message) { | |
super(source); | |
this.message = message; | |
} | |
public String getMessage() { | |
return message; | |
} | |
} | |
@Component | |
public class CustomEventListener { | |
@EventListener | |
public void handleCustomEvent(CustomEvent event) { | |
System.out.println("커스텀 이벤트 수신 - " + event.getMessage()); | |
} | |
} | |
@SpringBootApplication | |
public class SpringEventDemoApplication { | |
public static void main(String[] args) { | |
SpringApplication.run(SpringEventDemoApplication.class, args); | |
} | |
@Bean | |
CommandLineRunner commandLineRunner(ApplicationContext ctx) { | |
return args -> { | |
EventPublisher publisher = ctx.getBean(EventPublisher.class); | |
publisher.publishEvent("커스텀 이벤트!"); | |
}; | |
} | |
} |

참고
코딩으로 학습하는 GoF의 디자인 패턴 - 백기선 강사님
반응형
'Design Pattern' 카테고리의 다른 글
[디자인 패턴] 전략 패턴 (Strategy Pattern) (0) | 2024.07.03 |
---|---|
[디자인 패턴] 상태 패턴 (State Pattern) (0) | 2024.07.03 |
[디자인 패턴] 메멘토 패턴 (Memento Pattern) (1) | 2024.06.30 |
[디자인 패턴] 커맨드 패턴 (Command Pattern) (0) | 2024.06.30 |
[디자인 패턴] 이터레이터 패턴 (Iterator Pattern) (0) | 2024.06.30 |