Hello

옵저버 패턴 본문

java/디자인패턴

옵저버 패턴

nari0_0 2021. 1. 14. 18:12
728x90

옵저버 패턴이란

  • 한 객체의 상태가 바뀌면 의존하고 있는 모든 객체에 변경됨을 알려주는 일대다 의존성 관계를 정의한다.
  • 주제(subject) / 옵저버(observer)로 표현한다. 두 관계는 느슨하게 결합되어있다.
  • 주제는 옵저버가 인터페이스만 갖는다. 내부가 어떻게 구현되어있는지는 알지 않는다.
  • 주제는 옵저버 인터페이스를 구현한 객체라면 새로 구상된 옵저버 클래스를 추가 할 수 있다.
  • 서로 느슨한 결합이기 때문에 구현이 바뀌어도 주제 / 옵저버 구현만 되어있다면 영향을 미치지 않는다.
    • 예) 유튜브 채널을 구독했다면 새로운 영상이 등록 되었을 때 알람을 보낸다. 채널을 구독한 사람들은 새로운 영상이 올라왔다는 알람을 받게 된다.
    • 예) 집을 판매하기 위해 여러 부동산에 등록한다. 집을 판매하게 되면 부동산에 판매 완료 되었다는 연락을 해야한다.
  • 주제가되는 객체가 데이터를 보내는 방식(push 방식), 옵저버에서 데이터를 가져가는 방식(pull 방식) 두 가지 방법으로 구분된다.

옵저버 패턴의 기본 골자

push방법을 활용한 예시 구현 

영상 업로드 알람 다이어그램

Subject 인터페이스

public interface Subject {
    void addObserver(Observer observer);

    void removeObserver(Observer observer);

    void notifyObserver(Video video);
}

Observer 인터페이스

public interface Observer {
    void update(String name, Video video);
}

Subject 구현체

public class Channel implements Subject {
    private final List<Observer> observers;

    private String name;
    private final List<Video> videos;
    private final LocalDateTime createDate;

    public Channel(String name) {
        this.name = name;
        observers = new ArrayList<>();
        videos = new ArrayList<>();
        createDate = LocalDateTime.now();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Video> getVideos() {
        return videos;
    }

    public void addVideo(Video v){
        videos.add(v);
        notifyObserver(v);
    }

    public LocalDateTime getCreateDate() {
        return createDate;
    }

    @Override
    public void addObserver(Observer observer) {
        this.observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        this.observers.remove(observer);
    }

    @Override
    public void notifyObserver(Video video) {
        for(Observer o : observers){
            o.update(name, video);
        }
    }
}

 

Observer 구현체

  • 채널 구독 해제를 위해 Subject를 필드로 갖고 removeChannel() 메소드를 작성했다.
public class SubscriberAlertObserver implements Observer {
    private String name;
    private Subject subject;
    private final List<Video> noticeVideos;

    public SubscriberAlertObserver(String name, Subject subject) {
        this.name = name;
        this.subject = subject;
        subject.addObserver(this);
        noticeVideos = new ArrayList<>();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void removeChannel(){
        this.subject.removeObserver(this);
    }

    @Override
    public void update(String name, Video video) {
        noticeVideos.add(video);
        notice(name, video);
    }

    public void notice(String channelName, Video video) {
        System.out.println(name + "님 " + channelName + "님이 " + video.getTitle() + "을 업로드 했습니다.");
        System.out.println(video.getUploadDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    }

    public void unfoldAlert() {
        for (Video v : noticeVideos) {
            System.out.println(v.getTitle());
            System.out.println(v.getUploadDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        }
    }
}

Video 클래스

public class Video {
    private Integer no;
    private String title;
    private Boolean status;
    private final LocalDateTime uploadDate;

    public Video() {
        uploadDate = LocalDateTime.now();
    }

    public Integer getNo() {
        return no;
    }

    public void setNo(Integer no) {
        this.no = no;
    }

    public String getTitle() {
        return title;
    }

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

    public Boolean getStatus() {
        return status;
    }

    public void setStatus(Boolean status) {
        this.status = status;
    }

    public LocalDateTime getUploadDate() {
        return uploadDate;
    }
}

Main

    public static void main(String[] args) throws InterruptedException {
        Channel channel = new Channel("죠르디TV");
        SubscriberAlertObserver observerOne = new SubscriberAlertObserver("banana", channel);
        SubscriberAlertObserver observerTwo = new SubscriberAlertObserver("mango", channel);
        SubscriberAlertObserver observerThree = new SubscriberAlertObserver("orange", channel);
        for (int i = 0; i < 5; i++) {
            Video video = new Video();
            video.setTitle((i + 1) + "번째 영상");
            channel.addVideo(video);
            Thread.sleep(3000L);
            if (i == 3) {
                observerThree.removeChannel();
            }
        }
    }

자바 내장 옵저버 패턴 활용 예시 구현

728x90

'java > 디자인패턴' 카테고리의 다른 글

서킷 브레이커 패턴 (Circuit Breaker Pattern)  (0) 2023.10.16