[JAVA/디자인패턴] Observer Pattern, 관찰자 패턴

2023. 10. 6. 09:42
반응형

Observer Pattern

 

https://en.wikipedia.org/wiki/Observer_pattern

Observer 패턴은 객체 간에 일대다 의존성을 정의하며, 어떤 객체의 상태가 변경되면 그 객체에 의존하는 다수의 객체들이 자동으로 알림을 받아 업데이트될 수 있도록 하는 디자인 패턴이다. 해당 디자인 패턴에는 두 가지 주요 역할이 있다.

Observer(관찰자): 관찰 대상인 Subject의 상태 변화를 감시하고, 변화가 있을 때마다 적절한 Action을 취하도록 설계한다.
구현: Observer 인터페이스나 추상 클래스를 통해 정의되며 이 인터페이스에는 주로 update() 메서드가 포함되어 있어서 Subject로부터의 업데이트를 처리한다.


Subject(주제): 상태 변화가 일어날 때, 그 변화를 Observer들에게 알리는 역할을 한다. Subject는 Observer들을 등록하고, 상태가 변경되었을 때 등록된 모든 Observer들에게 알림을 보낸다. 주로 인터페이스를 통해 Sementic Subject가 정의되며 요구사항에 맞추어 Subject를 구현하여 사용한다.

 

 


Observer Pattern의 예시

구현 : 미세먼지 농도를 업데이트하는 Publisher가 있고, 업데이트된 미세먼지 농도를 Publisher로 부터 받아 상태를 업데이트하는 Subscriber들이 여러 개 있다. 

 

 

Subject 정의

 

public interface Subject {
    void addObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();
}

Sementic Subject를 정의한다. Update 대상 Observer(Subscriber)들을 등록하고 삭제하는 메서드를 정의하고, 상태 업데이트를 알리는 메서드도 함께 정의한다.

 

public class PollutionSubject implements Subject {
    private int pollution;

    private List<Observer> observers;

    public PollutionSubject() {
        observers = new ArrayList<Observer>();
    }

    public void addObserver(Observer o) {
        if (!observers.contains(o)) {
            observers.add(o);
        }
    }

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


    public void notifyObservers() {
        for (Observer o : observers) {
            o.updatePollution(pollution);
        }
    }


    public void setPollution(int n) {
        this.pollution = n;
    }
    

}

위의 Subject 인터페이스를 구현한 내용이다.

멤버변수로 변경사항을 알릴 대상 Observer 객체들이 등록되며, notifyObservers()를 통해 변경 알림 시, 등록된 모든 Observer 객체들에게 업데이트 메서드를 실행하도록 하고 있다.

 

 

 


Observer 정의

 

public interface Observer {
    void updatePollution(int n);
}

Observer 인터페이스도 정의해준다. updatePollution은 위의 Subject에서 호출하던 메서드로, Subject의 update 시 실행할 메서드이다.

 

 

 

public class AirPollutionSubscriber implements Observer {
    private int num;

    public AirPollutionSubscriber(int num) {
        this.num = num;
    }


    @Override
    public void updatePollution(int n) {
        System.out.println("Client pollution Receive " + n);
        this.num = n;
    }
}

이제 Observer를 구현한 클래스를 만든다. Observer은 Pub-Sub 관계로 보면 Subscriber에 해당하며, Publisher(Subject) 측에서 등록된 Subscriber들에게 update를 수행하도록 한다.

 

 


 

public class AirPollutionPublisher implements Runnable {
    private int pollution = 50;
    private boolean stop = false;
    private int sleepDuration;

    private PollutionSubject pollutionSubject;

    public AirPollutionPublisher(int duration, PollutionSubject pollutionSubject) {
        sleepDuration = duration;
        this.pollutionSubject = pollutionSubject;   //Subject를 등록한다.
    }

    @Override
    public void run() {
        while (!stop) {
            int plusMinus = RandIntInRange.nextInt(0, 1);
            int pollutionDiff = RandIntInRange.nextInt(1, 10);
            if (plusMinus == 1) {
                pollution += pollutionDiff;
            }
            else {
                pollution -= pollutionDiff;
                if (pollution < 0) {
                    pollution = 0;
                }
            }

            System.out.printf("Server: pollution = %d\n", pollution);
            
            //Subject가 대상 Observer들에게 변경하도록 notifyObservers()를 호출한다.
            pollutionSubject.setPollution(pollution);
            pollutionSubject.notifyObservers();

            try {
                Thread.sleep(sleepDuration);
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void stopThread() { stop = true; }
}

다음은 Pub-Sub 패턴에서 Publisher에 해당하는 역할을 하는 클래스이다.

간략하게 설명하면, 스레드를 이용해 일정 간격으로 미세먼지 농도를 업데이트하고, 이를 Subscriber들에게 알려주는 역할을 수행하는 클래스이며, 주석된 부분들을 유의해서 보면 된다.

 

 

 

 

class AirMain {

    public static void main(String[] args) {
        final int DURATION = 300;
        final int SLEEP_TIME = 10000;
        final int CLIENT_COUNT = 2;

        PollutionSubject subject = new PollutionSubject();


        // 미세먼지 농도 변경 쓰레드 실행
        AirPollutionPublisher pub = new AirPollutionPublisher(DURATION, subject);
        Thread pubThread = new Thread(pub);
        pubThread.start();
        
        AirPollutionSubscriber[] subs = new AirPollutionSubscriber[CLIENT_COUNT];
        for (int i = 0; i < CLIENT_COUNT; i++) {
            subs[i] = new AirPollutionSubscriber(i + 1);
            subject.addObserver(subs[i]);
        }


        try {
            Thread.sleep(SLEEP_TIME);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        // 미세먼지 농도 변경 쓰레드 종료
        pub.stopThread();



    }
}

이제 마지막으로 Main 함수이다. 실행 과정은 아래와 같다.

 

1. Publisher와 Subscriber을 연결할 Subject를 생성한다.

2. Publisher에 해당하는 인스턴스를 생성하고, Subject를 등록해준다. 이후 스레드를 실행시켜 미세먼지 농도를 주기적으로 업데이트하도록 한다.

3. Subscriber들에 해당하는 인스턴스들을 생성한 후, Subject에 Observer로서 추가해준다. 해당 과정까지 거쳐 AirPollutionSubscriber[]들은 AirPollutionPublisher와 Pub-Sub 관계를 이루게 된다.

4. 이제 스레드가 실행되면서 Publisher의 미세먼지 농도가 업데이트 될 때, Subject들의 미세먼지 농도도 함께 업데이트 될 것이다.

반응형

BELATED ARTICLES

more