관찰자 패턴

Observer란 관찰(Observe)하는 사람, 관찰자를 의미한다. 이 관찰자 패턴은 관찰 대상의 상태가 변하면 관찰자에게 통지되므로 상태 변화에 따른 처리를 기술할 때 유효하다.

관찰자 패턴에 사용되는 역할

####Subject(관찰 대상자)의 역할 Subject 역할은 관찰의 대상 을 나타낸다. Subject 역할은 관찰자인 Observer 역할을 등록하는 메소드와 삭제하는 메소드를 가지고 있다. 또한 현재의 상태를 취득할 메소드도 선언되어 있다.

####ConcreteSubject(구체적인 관찰대상자)의 역할 ConcreteSubject는 구체적인 관찰의 대상 을 나타낸다. 상태가 변하면 이 것을 등록되어 있는 Observer 역할에게 전달한다.

####Observer(관찰자)의 역할 Observer는 Subject 역할로부터 상태 변화를 통보 받는 역할을 한다. 그래서 update 메소드가 존재한다.

####ConcreteObserver(구체적인 관찰)의 역할 ConcreteObserver는 구체적인 Observer의 역할을 한다. update 메소드가 호출되면 그 메소드 안에서 subject 역할이 현재의 상태를 취득한다.

예제

flickr #####Observer 인터페이스

package io.hibrain.tutorial.designpattern.observer;

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

Observer 인터페이스는 관찰자 를 표현하는 인터페이스다. update 메소드는 NumberGenerator가 내용이 갱신 되었을 때 표시도 갱신하기 위해 Observer에 전달하기 위한 메소드다.

#####NumberGenerator 클래스

package io.hibrain.tutorial.designpattern.observer;

import java.util.Iterator;
import java.util.Vector;

public abstract class NumberGenerator {
    private Vector observers = new Vector();
    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();
}

NumberGenerator 클래스는 수를 생성하는 추상 클래스다. 실제 수의 생성과 수를 취득하는 부분은 하위 클래스가 구현할 것을 기대해서 추상 메소드로 되어 있다. observer 필드는 NumberGenerator를 관찰하고 있는 Observer들을 보관하고 있는 필드다 . addObserver는 Observer를 추가하는 메소드이고 deleteObserver는 Observer를 삭제하는 메소드다. notifyObserver 메소드는 Observer 전원에게 ‘내용이 갱신 되었으니 알려서 갱신해달라’라고 전달하는 메소드다.

#####RandomNumberGenerator 클래스

package io.hibrain.tutorial.designpattern.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 < 20; i++) {
            number = random.nextInt(50);
            notifyObservers();
        }
    }
}

RandomNumberGenerator 클래스는 NumberGenerator의 하위 클래스로 난수를 생성한다. random 필드엔 java.util.Random 클래스의 인스턴스가 저장되고 number필드에서 현재의 난수 값이 저장 된다. getNumber 메소드는 number 필드의 값을 반환한다.

#####DigitObserver 클래스

package io.hibrain.tutorial.designpattern.observer;

public class DigitObserver implements  Observer {
    public void update(NumberGenerator generator ) {
        System.out.println("DigiObserver:" + generator.getNumber());

        try {
            Thread.sleep(100);
        }catch ( InterruptedException e) {
        }
    }
}

DigitObserver 클래스는 Observer 인터페이스를 구현하고 있는 클래스로 관찰한 수를 ‘숫자’로 표시하기 위한 클래스다. update 메소드 안에서 인수로 제공된 NumberGenerator의 getNumber 메소드를 사용해서 수를 취득하고 그것을 표시한다.

#####GraphObserver 클래스

package io.hibrain.tutorial.designpattern.observer;

public class GraphObserver implements Observer {
    public void update(NumberGenerator generator) {
        System.out.println("GraphObserver:");

        int count = generator.getNumber();

        for (int i = 0; i < count; i++) {
            System.out.print("*");
        }
        System.out.println("");
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        }
    }
}

GraphObserver 클래스는 Observer 인터페이스를 구현하고 있는 클래스로 관찰한 수를 *****로 표시하기 위한 클래스다.

#####Main 클래스

package io.hibrain.tutorial.designpattern.observer;

public class HBNMain {
    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();
    }
}

Main 클래스에서는 RandomNumberGenerator의 인스턴스를 1개, 관찰자를 2개 만든다. 한 관찰자에서는 숫자로 표시하는 DigitObserver의 인스턴스를, 다른 관찰자에서는 GraphObserver의 인스턴스를 만들고 addObserver 메소드를 사용하여 관찰자를 등록 후 generator.execute를 사용하여 수를 생성한다.

참고자료

Java 언어로 배우는 디자인 패턴, Yuki Hiroshi ,2001