본문 바로가기
Design Pattern/행동 패턴(Behavioral patterns)

Observer Pattern, 옵저버 패턴

by codeyaki 2024. 4. 1.
반응형

옵저버 패턴이란?

스타크래프트를 했던 사람이라면 옵저버에 대해서 알고 있을것이다. 옵저버는 상대방을 감시하기 위해서 사용한다.

마찬가지로 옵저버 패턴은 주체의 상태 변화를 감지하고, 그 변화에 대해서 옵저버들에게 알림을 주는 디자인 패턴이다. 주로 여러 객체에게 변경을알려줘야 할때 사용한다. 스타크래프트에선 알림을 주진 않지만 여기선 감시하면서 알림까지 준다!

 

옵저버 패턴을 사용했을 때의 장점

  • 느슨한 결합을 유지할 수있다. 주체는 옵저버의 인터페이스만 알면되기 때문이다.
  • 쉽게 옵저버를 추가하거나 제거할 수 있다. (개방 폐쇄 원칙)

언제 옵저버 패턴을 고려해야 하는가?

  • 어떤 객체의 상태 변경이 하나 이상의 다른 객체에 영향을 미칠 때
  • 여러 객체들이 하나의 객체를 관찰해야 하고, 관찰 대상 객체는 관찰자의 정확한 수나 유형을 알 필요가 없을 때
  • 동적으로 관찰자를 추가하거나 제거해야 할 필요가 있을 때

옵저버 패턴을 사용했을 때의 단점

  • 사용하지 않는 옵저버로 인해 메모리 누수가 발생할 수 있다.
  • 옵저버의 수가 많으면 시스템이 느려질 수 있다.

비슷한 패턴과 비교하기

  • Publish-Subscribe 패턴
    • 구독-발행 패턴은 메시지 브로커를 사용해서 출판자와 구독자사이에서 메시지를 중계하는 패턴이다. 옵저버패턴은 어쨋든 옵저버의 인터페이스를 사용해서 참조가 이뤄지는데 구독발행의 경우 브로큰과 참조가 이뤄지기 때문에 더 느슨하게 이벤트 통신을 가능하도록 한다. (서로를 알 필요가 없다. 메시지 브로커만 알면 되기 때문이다.)
  • 커맨드 패턴
    • 커맨드 패턴을 커맨드를 캡슐화하여 처리하는 방식이다. 이는 다양하게 처리하기 위해서 사용하는 반면, 옵저버 패턴은 알림을 위해서 사용한다.

구현 예시 (java)

간단한 예시를 만들기위해 글에 키워드 알람을 걸어놓고 해당 키워드가 제목에 포함된 글이 올라오면 알람을 받는 로직을 구현해보자.

먼저 글을 만들어준다.

package com.example.designpattern.behavioral.observer.subject;

import java.time.LocalDateTime;

public class Post {
    private String title;
    private String content;
    private LocalDateTime regDate;
    private LocalDateTime updateDate;

    public Post(String title, String content) {
        this.title = title;
        this.content = content;
        this.regDate = LocalDateTime.now();
        this.updateDate = LocalDateTime.now();
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public LocalDateTime getRegDate() {
        return regDate;
    }

    public void setRegDate(LocalDateTime regDate) {
        this.regDate = regDate;
    }

    public LocalDateTime getUpdateDate() {
        return updateDate;
    }

    public void setUpdateDate(LocalDateTime updateDate) {
        this.updateDate = updateDate;
    }
}

 

그리고 이 글에 대한 옵저버 객체를 만들어준다.

 

package com.example.designpattern.behavioral.observer.observer;

import com.example.designpattern.behavioral.observer.subject.Post;

public interface Observer {
    void update(Post post);
}

 

package com.example.designpattern.behavioral.observer.observer;

import com.example.designpattern.behavioral.observer.subject.Post;

public class KeywordUser implements Observer{
    String name;
    String keyword;

    public KeywordUser(String name, String keyword) {
        this.name = name;
        this.keyword = keyword;
    }

    @Override
    public void update(Post newPost) {
        if(newPost.getTitle().contains(keyword)) System.out.println(name + " keyword title: " + newPost.getTitle());
    }
}

새로운 포스트가 올라오면 해당 글에 키워드가 포함되어있는지 확인후 포함되어 있다면 콘솔에 띄어주는 메소드를 가지고 있다.

 

마지막으로 글이 올라오면 옵저버들에게 알려줄 주체(게시판)을 만들어 주자

package com.example.designpattern.behavioral.observer.subject;

import com.example.designpattern.behavioral.observer.observer.Observer;

import java.util.ArrayList;
import java.util.List;

public class Board {
    private List<Observer> observers = new ArrayList<>();
    private List<Post> posts = new ArrayList<>();

    public void registerObserver(Observer o) {
        observers.add(o);
    }


    public void removeObserver(Observer o) {
        observers.remove(o);
    }

    public void notifyObservers(Post post) {
        for (Observer observer : observers) {
            observer.update(post);
        }
    }

    public void addPosts(Post post) {
        this.posts.add(post);
        notifyObservers(post);
    }
}

 

 

옵저버패턴을 사용하는 클라이언트단의 로직은 다음과 같다.

package com.example.designpattern.behavioral.observer;

import com.example.designpattern.behavioral.observer.observer.KeywordUser;
import com.example.designpattern.behavioral.observer.observer.Observer;
import com.example.designpattern.behavioral.observer.subject.Board;
import com.example.designpattern.behavioral.observer.subject.Post;

public class ObserverClient {
    public static void main(String[] args) {
        Board board = new Board();

        // 키워드 알림 신청 게시판 유저!
        Observer user1 = new KeywordUser("유저1", "이이팟");
        Observer user2 = new KeywordUser("유저2", "이이패드");
        Observer user3 = new KeywordUser("유저3", "이이팟");
        board.registerObserver(user1);
        board.registerObserver(user2);
        board.registerObserver(user3);

        board.addPosts(new Post("이이팟 팝니다", "새상품 25만원에 팔아요"));
        board.addPosts(new Post("이이패드 팝니다", "한번만 써본 상품 52만원에 팔아요"));
        board.addPosts(new Post("축구공 팝니다", "축구 딱한번 해봤습니다. 5만원에 팔아요"));

        board.removeObserver(user3);

        board.addPosts(new Post("이이팟 오른쪽만 팝니다", "오른쪽 유닛만 남았네요... 팝니다 5만원"));

    }
}

 

실행 결과는 다음과 같다.

유저1 keyword title: 이이팟 팝니다
유저3 keyword title: 이이팟 팝니다
유저2 keyword title: 이이패드 팝니다
유저1 keyword title: 이이팟 오른쪽만 팝니다

유저1과 유저3이 이이팟을 사려고 키워드 알람을 해두었고 누군가 이이팟을 판다고 글을올려 유저3이 구매를 해서 키워드 알람을 제거하였고 또 이이팟 글을 올렸을때에는 유저1만 키워드 알림을 받는것을 볼 수 있다.

 

축구공을 판매하는 글은 아무도 알림을 신청하지 않았기 때문에 키워드알람이 울리지 않았다.

 

이렇게 옵저버들을 만들고 간편하게 사용할 수 있다.

반응형