gimmickbutreal

[CS/Java] 옵저버 패턴(Observer Pattern) 본문

CS

[CS/Java] 옵저버 패턴(Observer Pattern)

isshosng 2022. 7. 24. 19:37

정의

옵저버 패턴은 객체의 상태 변화를 관찰하는 관찰자들, 즉 옵저버들의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버에게 통지하도록 하는 디자인 패턴을 말합니다. 

e.g.) 유튜브 채널을 구독 후 알림설정 버튼을 누르면 유튜브 알림이 뜨게 설계한다고 가정할 경우,

         주제 객체는 유튜브 채널, 옵저버는 구독자가 된다. 

 

주로 분산 이벤트 핸들링 시스템을 구현하는 데 사용되며 발행/구독 모델로 알려져 있기도 합니다. 

 

구조

이 패턴의 핵심은 옵저버 또는 리스너(Listener)라고 불리는 하나 이상의 객체를 관찰 대상이 되는 객체에 등록시키는 것입니다. 

 

옵저버 패턴 구조

 

구현 원리

 

이벤트를 발생시키는 Class B가 있고, 해당 클래스가 발생하는 이벤트를 수신 받고 싶어하는 Class A가 있다고 가정해보겠습니다.

 

아래 그림처럼, Class A에서 B의 이벤트를 수신 받기 위해 클래스 B를 인스턴스화 한 후, B가 A에게 자신이 갖고 있는 메서드를 호출하도록 요청합니다.

 

위와 같이 설계하면 아래와 같은 일이 발생한다.

 

 

Class B는 정상적으로 이벤트를 발생하고 있지만, A가 B를 일방적으로 인스턴스화 했기 때문에 B는 자신을 인스턴스화한 대상에게 접근할 수 없다. 이로 인해 B는 A의 메서드를 호출하지 못해 구독자에게 알림을 보낼 수 없다. 이럴 때 개발자들은 인터페이스를 이용해 옵저버 패턴을 구현한다.

 

 

A는 B에게 이벤트가 발생할 때마다 본인이 만든 종을 울리도록 요구한다. 그럼 B는 종을 울리게 되고 아까와는 달리 이벤트가 발생할 경우 이를 알려줄 수단이 생기게 되었습니다. 이는 두 클래스 사이에 인터페이스를 추가하여 문제를 해결했기 때문입니다. 

 

A는 인터페이스를 상속하여 이벤트가 발생할 때마다 실행시키는 메서드를 클래스 내에 구현한다. 그리고 B를 생성할 때 (A가 먼저 만들어짐) 인터페이스 구현체를 전달한다. B는 이벤트가 발생할 때마다 생성자로 전달받은 A가 구현한 인터페이스 메서드를 호출합니다. 

 

이 때, 이 인터페이스(위 예시에서 종)을 옵저버라고 부른다. Listener라고도 부르는데, 관찰자나 청자나 비슷한 맥락이라고 생각하면 됩니다. 안드로이드에서 리스너를 주로 사용하기 때문에 편의상 앞으로 리스너라고 말하겠습니다.

 

코드 구현

1. 리스너 인터페이스 만들기

우선, 이벤트를 수신 받는 클래스와 이벤트를 발생하는 클래스를 이어줄 리스너 인터페이스를 만들어줍니다.

I_ButtonListener라는 인터페이스를 만든 후 클릭이 이루어지는 clickEvent 메서드를 만들어줍니다.

clickEvent는 이벤트로부터 리스너를 전달 받을 수 있는 메서드입니다. 

 

1
2
3
public interface I_ButtonListener {
    void clickEvent(String event);
}
cs

 

2. 버튼 클래스 만들기

Button이라는 클래스를 만들고 전역 변수에 name과 buttonListener를 구성합니다.

생성자인 Button는 이름을 받고 클릭(clickevent) 했을 때 메세지를 받는 click 메서드는 buttonListener의 clickEvent로 전달 받을 수 있도록 설계하고 addListener라는 메서드를 만들어줍니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Button {
    private String name;
    private I_ButtonListener buttonListener;
 
    public Button(String name){
        this.name = name;
    }
 
    public void click(String message){
        buttonListener.clickEvent(message);
    }
 
    public void addListener(I_ButtonListener buttonListener){
        this.buttonListener = buttonListener;
    }
}
cs

 

3. Main 클래스 만들기

main 메서드에 Button 객체를 생성해주고, 그 아래에 리스너를 달기 위해서 button.addListener 메서드에 I_ButtonListener()를 람다로 전달받을 수 있게끔 설계합니다.

그리고 이벤트가 발생할 때 로그로 직접 확인할 수 있도록 println 메서드를 사용했습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Main {
    public static void main(String[] args) {
        Button button = new Button("BUTTON");
 
        button.addListener(new I_ButtonListener() {
            @Override
            public void clickEvent(String event) {
                System.out.println(event);
            }
        });
 
        button.click("Message forwarding : click1");
    }
}
cs

 

4. 실행결과

 

참고 : https://url.kr/c7nrg2

https://url.kr/81mjvn

https://url.kr/d9lfxb

https://codedot.tistory.com/8 

 

'CS' 카테고리의 다른 글

스택과 큐 Stack & Queue  (0) 2023.07.12
연결리스트 LinkedList  (0) 2023.07.12
복잡도  (0) 2023.07.06
[CS/DB] SQL과 NoSQL의 차이  (0) 2022.07.31
[CS/Java] Collection Framework란 무엇일까?  (0) 2022.07.14