(인프런) 코딩으로 학습하는 GoF의 디자인 패턴 - 백기선, 강의를 보고 정리한 글입니다.
코드는 GitHub 에 있습니다
#1. 객체 생성 관련 패턴 |
#2. 구조 관련 패턴 |
#3. 행동 관련 패턴 |
||
✔️ 옵저버 패턴이란 (Observer Pattern)
다수의 객체가 특정 객체 상태 변화를 감지하고 알림을 받는 패턴.
- 👏🏻 옵저버 디자인 패턴이란, 객체의 상태 변화를 관찰하는 관찰자 객체를 생성하여, 특정한 객체의 상태 변경을 지켜보는 디지인 패턴입니다.
- 객체의 상태변화를 관찰하는 옵저버(관찰자)들의 목록을 객체에 등록하여 상태 변화가 있을 때 마다 notify를 통해 객체가 직접 목록의 각 옵저버에게 통지하고 → 각각의 옵저버는 이러한 "이벤트"가 발생했을 시 처리할 동작을 수행합니다.
- 이러한 구조로 객체의 상태가 변화하면, 종속객체들이 자동으로 변화가 통지되어 그에 따른 명령을 수행하도록하는 일대다(One-to-Many)의 의존성을 정의해 줍니다.
- 즉, 관찰하고 있는 객체의 상태변경에 실시간으로 대응하기 위한 목적을 가지는 패턴입니다.
📌 "상태가 변화되는 객체" 와 이 객체의 상태에 "의존성이 있는 객체" → 이 두 객체가 직접적으로 참조되지 않고 중간에 관찰자 객체를 둠으로써 느슨한 연결구조를 설계합니다.
옵저버 패턴이 사용되는 경우
- pub-sub (발행[publish] - 구독[subscribe]) 이벤트 기반 프로그래밍에 주로 사용되는 패턴입니다.
- 분산 이벤트 핸들링 시스템에서도 자주 사용이 된다고 합니다.
- 옵저버 패턴은 MVC 패러다임과 자주 결합되어, Model 과 View 사이를 느슨하게 연결하기 위해 주로 사용된다고 합니다.
✔️ 옵저버 패턴 예시
1) 옵저버 패턴 적용
- 간단한, 작은 단체 채팅방을 예시로 들어보겠습니다.
- 구조는 subject 역할을 하는 ChatServer 가 존재하고
- chatServer 의 register 즉, 단체톡방을 Observer 로
- 단체톡방에 종속된 객체인 ConcreateObserver 를 User 클래스롤 구현하겠습니다.
그럼 ChatServer 이벤트 발생 → 등록된 Observer가 관찰 → ConcreteObserver 들의 행위 실행이 됩니다.
👇
Observer
- 먼저 옵저버 클래스입니다.
- Subscriber 라는 인터페이스로 정의하고, notify 메소드를 저으이합니다.
//Observer
public interface Subscriber {
void notifyHandleMessage(String message);
}
ConcreteObserver
- 그 다음은 Observer 를 상속받아 각각의 행위를 캡슐화하여 구현한 ConcreteObserver 클래스 입니다.
- 저는 User라는 이름의 개체를 만들어주었고
- 이벤트가 발생했을 때 실행할 notify를 정의해 주었습니다.
//ConcreteObserver
@Getter
@AllArgsConstructor
public class User implements Subscriber{
private String name;
@Override
public void notifyHandleMessage(String message) {
System.out.println("받는사람 (" + name + ") " + message );
}
}
Subject
- subject 클래스로 채팅서버를 두었습니다.
- Subject 클래스에서 이벤트 등록, 해지, 발생의 책임을 담당합니다.
public class ChatServer {
private Map<String, List<Subscriber>> subscribers = new HashMap<>();
//등록 - Observer 등록
public void register(String group, Subscriber subscriber) {
if (subscribers.containsKey(group)) {
subscribers.get(group).add(subscriber);
return;
}
List<Subscriber> list = new ArrayList<>();
list.add(subscriber);
this.subscribers.put(group, list);
}
//해지
public void unregister(String subject, Subscriber subscriber) {
if (subscribers.containsKey(subject)) {
subscribers.get(subject).remove(subscriber);
}
}
//이벤트 발생
public void sendMessage(User user, String group, String message) {
String userMessage = "보내는사람 ("+user.getName() + ") : " + message;
// observer list 에 등록된 각각의 옵저버들의 notify 실행
if (subscribers.containsKey(group)) {
System.out.println("========= 단톡방 : " + group + " =======");
this.subscribers.get(group).forEach(s -> s.notifyHandleMessage(userMessage));
}
}
}
Client (실행)
- 각각의 유저를 정의하고, 해당 유저를 ChatServer 의 Observer(채팅방) 에 등록합니다.
- ChatServer (Subject) 의 특정한 이벤트 (sendMessage) 가 발행될 떄, 이를 바라보고 있던 Observer 클래스들의 notify 가 실행됩니다.
public class Client {
public static void main(String[] args) {
ChatServer chatServer = new ChatServer();
User user1 = new User("keesun");
User user2 = new User("helloMan");
chatServer.register("오징어게임", user1);
chatServer.register("오징어게임", user2);
chatServer.register("디자인 패턴", user1);
chatServer.sendMessage(user1, "오징어게임", "아 이름이 기억났어 ~~ 오일남이야");
System.out.println();
chatServer.sendMessage(user2, "디자인 패턴", "오저버 패턴으로 만든 패턴");
}
}
✔️ 옵저버 패턴 장단점
장점
- 상태를 변경하는 객체와 변경을 감지하는 객체의 의존성을 느슨하게 유지할수있습니다.
- → 강결합 되어있는 두 서비스를 분리하여 의존성과 결합도를 낮춘다.
- subject의 상태변경을 주기적으로 조회하지 않아도 자동적으로 감지할 수 있습니다.
- 런타임시에 옵저버를 추가하거나 제거할 수 있습니다.
단점
- 복잡도가 증가합니다.
- 패턴을 잘못 구현할 경우 데이터 배분에 문제가 발생하여 위험도가 큽니다.
- 다수의 Observer 객체를 등록 이후 해지 않는다면 memory leak이 발생할 수도 있다.
- 위의 예시에서, Map 에 담겨있는 Observer 가 명시적을 해제되지 않고, Map 안에서 Reference(참조)를 가지고 있다면 가비지 컬렉션의 대상 되지 않음
- 즉, 이벤트가 계속쌓이고, 메모리 관리가 되지 않는다면 시한폭탄이나 다름없다
'Java > Design Pattern' 카테고리의 다른 글
[디자인 패턴] 행동 패턴 - 전략 패턴 (Strategy Pattern) (0) | 2023.04.21 |
---|---|
[디자인 패턴] 행동 패턴 - 상태 패턴 (State Pattern) (0) | 2023.04.19 |
[디자인 패턴] 행동 패턴 - 메멘토 패턴 (Memento Pattern) (0) | 2023.04.12 |
[디자인 패턴] 행동 패턴 - 책임 연쇄 패턴 (Chain of Responsibility Pattern) (0) | 2023.04.11 |
[디자인 패턴] 행동 패턴 - 중재자 패턴 (Mediator Pattern) (0) | 2023.04.10 |