1. 옵저버 패턴(Observer Pattern)
- 주체가 어떤 객체의 상태 변화를 관찰하다가 변화가 있을 때마다 메서드 등을 통해 옵저버 목록에 있는 옵저버들에게 변화를 알려주는 디자인 패턴.
- 객체 간의 1:N 관계를 설정해서, 한 객체의 상태 변화가 발생하면 이를 의존하는 객체들에게 자동으로 알림을 보내는 패턴.
- 즉, 누가 변하면 자동으로 알려준다 ~
2. Java의 옵저버 패턴 예시
import java.util.ArrayList;
import java.util.List;
// 옵저버 인터페이스 (Observer)
interface Observer {
void update(String videoTitle);
}
// 유튜브 채널 (Subject)
class YouTubeChannel {
private List<Observer> subscribers = new ArrayList<>();
private String channelName;
public YouTubeChannel(String channelName) {
this.channelName = channelName;
}
// 구독자 추가
public void subscribe(Observer observer) {
subscribers.add(observer);
}
// 구독자 제거
public void unsubscribe(Observer observer) {
subscribers.remove(observer);
}
// 새 영상 업로드 -> 모든 구독자에게 알림
public void uploadVideo(String videoTitle) {
System.out.println(channelName + " 채널에 새로운 영상 업로드: " + videoTitle);
notifyObservers(videoTitle);
}
// 구독자들에게 알림 보내기
private void notifyObservers(String videoTitle) {
for (Observer subscriber : subscribers) {
subscriber.update(videoTitle);
}
}
}
// 구독자 클래스 (Observer 구현)
class Subscriber implements Observer {
private String name;
public Subscriber(String name) {
this.name = name;
}
@Override
public void update(String videoTitle) {
System.out.println(name + "님, 새로운 영상이 업로드되었습니다: " + videoTitle);
}
}
// 실행 클래스
public class ObserverPatternDemo {
public static void main(String[] args) {
// 유튜브 채널 생성
YouTubeChannel channel = new YouTubeChannel("자진모리의 개발채널");
// 구독자 생성
Subscriber user1 = new Subscriber("철수");
Subscriber user2 = new Subscriber("영희");
// 구독자 등록
channel.subscribe(user1);
channel.subscribe(user2);
// 영상 업로드 (자동으로 알림 전송됨)
channel.uploadVideo("옵저버 패턴 쉽게 이해하기!");
// 영희가 구독 취소
channel.unsubscribe(user2);
// 새로운 영상 업로드
channel.uploadVideo("스프링 부트로 웹 개발 시작하기!");
}
}
코드 분석
1. Observer(옵저버 인터페이스)
- 옵저버 패턴에서
Observer(구독자) 역할을 할 인터페이스 update(String videoTitle)메서드: 유튜브 채널에서 새로운 영상이 올라오면 실행
2. YouTubeChannel(Subject 역할 클래스)
Subject(주제, 발행자) 역할- 구독자 목록을
ArrayList로 저장하고, 채널 이름을 저장 subscriber: 구독자가 채널을 구독하면subscribers리스트에 저장됨unsubscribe: 구독자가 구독을 취소하면 리스트에서 삭제됨uploadVideo: 새로운 영상을 업로드할 때 콘솔에 출력하고,notifyObservers(videoTitle)을 호출해서 모든 구독자에게 알림 보냄notifyObservers:subscribers리스트에 있는 모든 옵저버(구독자)에게update(videoTitle)을 호출해서 알림을 전달
3. Subscriber(Observer 역할을 하는 구독자 클래스)
Observer인터페이스를 구현한 클래스- 구독자의 이름을 저장,
update()메서드를 오버라이드해서 새로운 영상이 올라왔을 때 알림을 받음
4. ObserverPatternDemo(실행 클래스)
YouTubeChannel을 생성하고 구독자(철수,영희)를 등록함- 이후에, 영희가 구독을 취소함
- 두번째 영상이 올라가면 철수만 알림 받음
3. 옵저버 패턴의 장단점
장점
1. 객체 간의 결합도가 낮아짐
- Subject와 Observer가 직접적인 의존 관계가 없음
- 새로운 Observer를 추가하거나 기존 Observer를 제거해도 Subject 코드 수정이 필요 없음 > 유지보수 쉬움 !
- 예제의 유튜브 채널이 구독자의 구체적인 동작을 몰라도 자동으로 알림을 보낼 수 있었던 것처럼 ~
2. 자동 알림
- Subject의 상태가 변경되면 자동으로 모든 Observer에게 알림을 보냄
- 개발자가 직접 일일이 객체들을 호출해서 업데이트하는 수고를 덜어낸다 !
- 예제의 유튜브 채널이 새 영상을 업로드하면 구독자들에게 자동으로 알림이 가는 것처럼~
3. 확장성이 뛰어남
- 새로운 Observer를 추가할 때 기존 코드를 변경할 필요 없이 그냥 추가하면됨 !
- 여러 개의 Observer를 등록할 수 있어서 하나의 Subject를 여러 곳에서 활용 가능하다
4. 변화에도 유연한 설계
- Subject 내부 로직이 바뀌어도 Observer는 영향을 받지 않는다
- 각 객체의 역할이 분리되어 있어서 코드 수정이 유연함 !
단점
1. Observer가 많아지면 성능 저하..
- Observer가 많아질수록 업데이트를 한꺼번에 보내야 하기 때문에 성능 문제가 발생할 수 있음
- ex) 유튜브 채널이 수백만 명의 구독자를 가지고 있다면 영상이 올라갈 때마다 엄 청 난 트래픽이 발생할 수 있음 > 특정 Observer에게만 알림을 보내거나, 비동기 방식으로 처리하기
2. 디버깅이 어려울 수도..
- Observer 패턴을 사용하면 이벤트가 어디서 발생했는지 추적하기 어려울 수 있음
- ex) Subject가 변경되었을 때 여러 Observer가 자동으로 반응하기 때문에 어떤 Observer가 어떤 순서로 호출되었는지 확인하기 어려울 수 있음
3. 메모리 누수 위험
- Subject와 Observer가 서로를 참조하면서 객체가 해제되지 않는 메모리 누수 가능성.
- Observer를 제거하지 않고 계속 남겨두면 불필요한 객체가 계속 메모리를 차지할 수도 있음 > ex)Observer가 필요 없을 때는 반드시 unsubscribe() 메서드를 호출해서 제거해 줘야 함
4. 모든 Observer가 필요하지 않은 업데이트도 받을 수 있음
- 어떤 옵저버는 특정 조건에서만 업데이트가 필요할 수도 있는데 무조건 notify()를 호출하면 불필요한 업데이트가 발생할 수도 있음 > Observer에게 필터링 기능을 추가하거나, 특정 조건에서만 업데이트하도록 설계
4. 옵저버 패턴을 사용하는 경우
1. 상태 변경을 여러 객체가 감지해야 할 때
- ex) 유튜브 채널, 뉴스 웹사이트
2. 이벤트 기반 시스템을 만들고 싶을 때
- ex) 버튼 클릭 이벤트, 게임에서 캐릭터 체력이 0이 되면 여러 시스템이 반응
3. MVC(Model-View-Controller) 패턴
- Model(데이터)이 변경될 때 View(화면)이 자동으로 업데이트되는 구조
4. Java의 상속(extends)과 구현(implements)
상속(extends)
- 기존(부모)클래스를 물려받아 새로운(자식)클래스를 만드는 것.
상속의 특징
- 부모 클래스의 기능(메서드, 필드)를 자식 클래스가 그대로 물려받음
- 자식 클래스는 부모 클래스의 기능을 재사용 + 새로운 기능을 추가할 수 있음
extends키워드를 사용- 상속받은 메서드를 오버라이딩(재정의) 할 수도 있음 !
- 즉, 부모님께 레시피를 물려받아 레시피 그대로 사용하거나 재료를 추가할 수 있음 ~!
구현(implements)
- 인터페이스(설계도)만 제공하고, 세부적인 기능은 클래스에서 직접 만드는 것.
구현의 특징
- 인터페이스는 설계도 역할을 하며, 기능의 '이름'만 정의하고 '구현'은 없음
implements키워드를 사용- 인터페이스를 구현하는 클래스는 반드시 인터페이스의 메서드를 오버라이딩(재정의)해야 함
- 즉, 레시피 책은 있지만 안에 내용이 없어서 어떤 재료로, 어떤 방식으로 만들지 내가 직접 해야 함
5. 상속과 구현, 언제 사용할까?
- 상속을 사용할 때(extends)
*is-a 관계: 객체 지향 프로그래밍에서 "어떤 클래스가 다른 클래스의 상속인지를 나타내는 개념"
즉, "A는 B다 라는 문장이 성립하면, A는 B의 자식 클래스일 가능성이 높다
- 기존 클래스를 그대로 사용하고 싶을 때
- 부모 클래스의 기능을 재사용하면서 추가적인 기능을 더할 때
- *is-a 관계가 성립할 때
- 구현을 사용할 때(implements)
*can-do 관계: 객체 지향 프로그래밍에서 "어떤 객체가 특정한 행동을 할 수 있는지를 나타내는 개념"
즉, "A는 B를 할 수 있다 라는 문장이 성립하는 경우"
- 공통된 기능(인터페이스)은 같지만 각각 다르게 동작해야 할 때
- 여러 개의 기능을 동시에 적용하고 싶을 때 > 다중 구현 가능
- *can-do 관계가 성립할 때
6. 결론
옵저버 패턴은 객체의 상태 변화가 있을 때 이를 다른 객체들에게 자동으로 알려주는 패턴이다.
한 객체(주체)가 상태 변화를 감지하고, 이를 의존하는 여러 객체(옵저버)들에게 알림을 보낸다.
상속은 자식 클래스가 부모 클래스의 기능을 재사용하고 확장할 때 사용된다.
구현은 인터페이스에서 정의된 기능을 클래스에서 실제로 구현할 때 사용된다
'Computer Science > Design Pattern' 카테고리의 다른 글
| 이터레이터 패턴(Iterator Pattern) (0) | 2025.03.21 |
|---|---|
| 프록시 패턴(Proxy Pattern) (0) | 2025.03.19 |
| 전략 패턴(Strategy Pattern) (1) | 2025.03.18 |
| 팩토리 패턴(Factory Pattern) (0) | 2025.03.17 |
| 싱글톤 패턴(Singleton Pattern) (1) | 2025.03.14 |
