Design Pattern | Observer Pattern (옵저버 패턴)

Observer 패턴이란?

  • Observer라는 영어 단어는 관찰자라는 의미이다.
  • Observer 패턴은 관찰 대상의 상태가 변할 때 관찰자에게 통지되는 방식이다.
  • Observer 패턴은 상태 변화에 따른 처리를 기술할 때 유효하다.
  • 객체의 상태 변화를 관찰하는 관찰자들, 즉 옵저버들의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버에게 통지하도록 하는 디자인 패턴이다.
  • 주로 분산 이벤트 핸들링 시스템을 구현하는데 사용된다. 발행/구독 모델로 알려져 있기도 하다.
  • GoF의 디자인 패턴에서는 행위에 대한 디자인 패턴으로 분류된다.

Observer 패턴 예제 프로그램

무작위로 숫자를 생성하고 그 변화에 따라 “숫자” 및 “*“를 표시하는 프로그램이다.

Class Diagram
Observer Pattern Class Diagram

1. Observer 인터페이스

관찰자를 나타내는 인터페이스이다.

Observer.java

package com.devkuma.designpattern.behavioral.observer;

public interface Observer {
    void update(NumberGenerator generator);
}

2. DigitObserver 클래스

수치를 숫자로 그대로 표현하는 클래스이다. 이 클래스는 Observer 인터페이스를 구현한다.

DigitObserver.java

package com.devkuma.designpattern.behavioral.observer;

public class DigitObserver implements Observer {

    public void update(NumberGenerator generator) {
        System.out.println("DigitObserver: " + generator.getNumber());
    }
}

3. GraphObserver 클래스

수치를 간단한 그래프로 표현하는 클래스이다. 이 클래스는 Observer 인터페이스를 구현한다.

GraphObserver.java

package com.devkuma.designpattern.behavioral.observer;

public class GraphObserver implements Observer {

    public void update(NumberGenerator generator) {
        System.out.print("GraphObserver: ");
        int count = generator.getNumber();
        for (int i = 0; i < count; i++) {
            System.out.print("*");
        }
        System.out.println("");
    }
}

4. NumberGenerator 클래스

숫자를 생성하는 객체를 나타내는 추상 클래스이다.

NumberGenerator.java

package com.devkuma.designpattern.behavioral.observer;

import java.util.ArrayList;
import java.util.Iterator;

public abstract class NumberGenerator {

    private ArrayList<Observer> observers = new ArrayList();

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

    public void deleteObserver(Observer observer) {
        observers.remove(observer);
    }

    public void notifyObservers() {
        Iterator it = observers.iterator();
        while (it.hasNext()) {
            Observer o = (Observer) it.next();
            o.update(this);
        }
    }

    public abstract int getNumber();

    public abstract void execute();
}

5. RandomNumberGenerator 클래스

무작위로 숫자를 생성하는 클래스이다.

RandomNumberGenerator.java

package com.devkuma.designpattern.behavioral.observer;

import java.util.Random;

public class RandomNumberGenerator extends NumberGenerator {

    private Random random = new Random();
    private int number;

    public int getNumber() {
        return number;
    }

    public void execute() {
        for (int i = 0; i < 10; i++) {
            number = random.nextInt(50);
            notifyObservers();
        }
    }
}

6. Main 클래스

메인 처리를 실행하는 클래스이다.

Main.java

package com.devkuma.designpattern.behavioral.observer;

public class Main {
    public static void main(String[] args) {
        NumberGenerator generator = new RandomNumberGenerator();
        Observer observer1 = new DigitObserver();
        Observer observer2 = new GraphObserver();
        generator.addObserver(observer1);
        generator.addObserver(observer2);
        generator.execute();
    }
}

7. 실행 결과

DigitObserver: 42
GraphObserver: ******************************************
DigitObserver: 19
GraphObserver: *******************
DigitObserver: 47
GraphObserver: ***********************************************
DigitObserver: 32
GraphObserver: ********************************
DigitObserver: 18
GraphObserver: ******************
DigitObserver: 34
GraphObserver: **********************************
DigitObserver: 23
GraphObserver: ***********************
DigitObserver: 9
GraphObserver: *********
DigitObserver: 17
GraphObserver: *****************
DigitObserver: 24
GraphObserver: ************************

Observer 패턴의 장점

Observer 패턴에서는 상태를 가지고 있는 RandomNumberGenerator 클래스와 상태 변화를 전달받는 클래스(DigitObserver, GraphObserver)가 등장한다. 그리고 그 2개의 역할을 이어지고 있는 것이 Observer 인터페이스와 NumberGenerator 클래스이다.
RandomNumberGenerator 클래스는 자신이 현재 감시하고 있는 것이 DigitObserver 인스턴스인지 GraphObserver 인스턴스인지를 모른다. 다만, observers 필드에 저장된 인스턴스가 Observer 인터페이스를 상속한다는 것을 알고 있으며 update 메서드를 호출할 수 있다.

한편, DigitObserver 클래스, GraphObserver 클래스는 자신을 관찰하고 있는 것이 RandomNumberGenerator 인스턴스인지 다른 XxxNumberGenerator 인스턴스인지는 모른다. 다만, NumberGenerator의 서브 클래스의 인스턴스이며, getNumber 메소드를 가지고 있는 것은 알고 있다.

  • 추상 클래스나 인터페이스를 사용하여 구상(Concrete) 클래스로부터 추상 메소드를 떼어낸다.
  • 인수로 인스턴스를 전달 할 경우나 필드로 인스턴스를 보관 유지할 경우에는 추상 클래스나 인터페이스의 형태로 해둔다. 이렇게 함으로써 구상 클래스의 부분을 정확하게 교환할 수 있게 된다.