CS/디자인패턴

[디자인패턴] Observer 패턴 (옵저버 패턴, Publish&Subscribe

mabb 2023. 6. 24. 13:06
반응형

▶디자인 패턴 ( Design Pattern)

:프로그램 개발 시 문제 해결을 위하여 빈번히 사용되는 개발자들의 경험, 내적인 축적에 대하여,
GoF(Gang of Four) 라 불리는 4인의 개발자들이 각각을 패턴으로 정의하고 이름을 붙였다.
이를 디자인 패턴 (Design Pattern) 이라고 한다.
 23개의 디자인 패턴을
『Elements of Reusable Object-Oriented Software』
라는 책으로 발간하였다.

Gang of Four 분들은 모두 안경을 썼다.

 

▷디자인 패턴의 용어를 빌리면 서로의 아이디어를 보다 용이하게 비교, 논의할 수 있게 된다.
▷재사용과 기능확장이 쉬운 소프트웨어를 만들기 위한 유익한 기법이 바로 디자인 패턴이다.

 

---------------------------------------------------------
1. Iterator
2. Adapter
3. Template Method
4. Factory Method
5. Singleton
6. Prototype
7. Builder
8. Abstract Factory
9. Bridge
10. Strategy
11. Composite
12. Decorator
13. Visitor
14. Chain of Responsibility
15. Facade
16. Mediator
17. Observer
18. Memento
19. State
20. Flyweight
21. Proxy
22. Command
23. Interpreter
---------------------------------------------------------

▶Observer 패턴

"Observer와 Subject"
"사실은 Subject가 변화를 알린다."
"Subject 변화에 대한 동기화에 주안점"


-왜 사용하는가
-클래스다이어그램
-각 클래스(인터페이스)의 역할
-Java소스

▶Observer 패턴에 대한 이해

-Observer는 관찰자이다.

-Publish/ Subscribe 패턴이라고도 한다.

-관찰되는 대상을 Subject라고 칭한다.

-Observer는 Subject의 동작 및 상태 변화를 관찰하고 동작을 수행한다.

-하지만 사실은 Observer는 능동적으로 관찰하는 것이 아니라 Subject에게 알림을 받는다.(update)

-Subject는 동작 및 상태 변화 시 본인이 add하여 가지고 있는  Observe들에게 이를 알린다.

-Observer는 Subject가 Observer를 update할 때(변화를 알릴 때) 동작한다.

-Subject는 여러 Observer를 가지고 있을 수 있다.

-'상태 변화를 알린다'는 점에서 Mediator패턴과 비슷하지만 Mediator는 여러 오브젝트들의 보고를 받아 해당 오브젝트들(Colleague)을 조정하고, Observer는 Subject가 여러 Observer를 가지고 Observer들에게 상태변화를 알리면 각각의 Observer가 Subject의 상태를 동기화하는 목적으로 사용한다.

-Subject는 본인이 사용할 Observer를 추가하고 삭제할 수 있다.

-Subject는 현재의 상태를 취득하는 메소드를 가지고 있다.

-Observer가Subject의 상태를 변화시킬 경우 메서드 호출이 반복될 수 있다. 이 경우 플래그 변수를 사용하는 것이 좋다.

-Subject가 어느 정보까지 Observer에게 알릴 지는 프로그램의 복잡성을 고려해서 설계한다.

-Observer패턴은 Subject의 변화를 Observer에게 알려서 상태를 동기화하는 일에 주안점을 둔다.

-Mediator패턴은 Colleague들의 변화를 가지고 Colleague들을 조정하는데 주안점을 준다.

-자바에서도 java.util.Observer 인터페이스로 옵저버 패턴을 구현해 두었다. 하지만 이 옵서버는 java.util.Observable을 확장한 Subject들과만 호환이 된다. java.util.Observable은 추상클래스로, 다중 클래스 상속이 되지 않는 자바의 특성상 이 라이브러리를 활용하는 것은 어렵다.

 

▶왜 사용하는가

오브젝트(여기서 Subject)의 상태가 변화할 때, 상태 변화 시점에 상태 변화를 확인하고 동기화하기 위해 사용한다. 

 

▶클래스다이어그램

Observer패턴의 클래스다이어그램, Subject는 Observer를 Aggregation

 

▶각 클래스(인터페이스)의 역할

▷옵저버 인터페이스 역할

update() 메서드를 구현하여 Subject들이 옵서버. update()로 변화 사실을 옵서버에게 알릴 수 있도록 메서드를 정한다.

▷구체적 옵저버 역할

Subject의 변화로 update() 시 구체적인 동기화 로직 등을 구현한다. 

▷Observable 역할

옵서버에게 알리기, 현재 본인의 상태를 추출하기, 옵서버를 추가 및 삭제하는 기능을 구현 및 정한다.

▷Observable Subject 역할

상태 변화 시 옵저버.update() 메서드를 호출하여 옵서버들에게 상태 변화를 알린다.

▷메인 역할 

예제에서는 Subject들에 Observer를 추가하고 Subject를 실행시키고 있다.

 

▶Java 소스

*java1.8 이전 버전을 사용하는 교재를 참고 하였습니다.
Java API 문서 등을 참조하여 제네릭을 적용하는 등 소스를 개선할 필요가 있습니다.
(https://docs.oracle.com/en/java/javase/20/docs/api/)

예제에서는 Observer를 구현한 세 개의 구체적 옵서버와 숫자를 생성하여 자신의 상태를 변화시키는 두 개의 Observable Subject가 등장한다. 이 두 Subject들은 Observer를 add 하여 가지고, 자신의 상태가 변화할 때마다 옵서버. update()로 상태 변화를 옵서버들에게 알린다. 옵서버들은 상태변화 감지 시 이를 System.out.println으로 출력하고 있다.

 

▷옵서버 역할 - Observer.java

package main.java.designpattern.observer;

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

}

 

▷구체적 옵저버 역할

DigitObserver.java

package main.java.designpattern.observer;

public class DigitObserver implements Observer{
    @Override
    public void update(NumberGenerator generator) {
        System.out.println("DigitObserver:" + generator.getNumber());
        try {
            Thread.sleep(100);
        }catch(Exception e){
        }
    }
}

 

GraphObserver.java

package main.java.designpattern.observer;

public class GraphObserver implements Observer {
    @Override
    public void update(NumberGenerator generator) {
        System.out.println("GraphicObserver: ");
        int count = generator.getNumber();
        for(int i=0; i<count; i++){
            System.out.print("*");
        }
        System.out.println("");
        try{
            Thread.sleep(100);
        }catch(Exception e){
        }
    }
}

 

OddObserver.java

package main.java.designpattern.observer;

public class OddObserver implements Observer{
    @Override
    public void update(NumberGenerator generator) {
        if(generator.getNumber()%2 ==1){
            System.out.println("OddObserver: 홀수 관찰" + generator.getNumber());
        }else{
        }
    }
}

 

 

▷Observable 역할 - NumberGenerator.java

package main.java.designpattern.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<Observer> it = observers.iterator();
        while(it.hasNext()){
            Observer o = it.next();
            o.update(this);
        }
    }
    public abstract int getNumber();
    public abstract void execute();
}

 

▷Observable Subject 역할

RandomNumberGenerator.java

package main.java.designpattern.observer;

import java.util.Random;

public class RandomNumberGenerator extends NumberGenerator{
    private Random random = new Random();
    private int number;

    @Override
    public int getNumber() {
        return number;
    }

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

 

IncrementalNumberGenerator.java

package main.java.designpattern.observer;

public class IncrementalNumberGenerator extends NumberGenerator{
    private int number;
    private int start,end,d;

    public IncrementalNumberGenerator(int start, int end, int d){
        this.start = start;
        this.end = end;
        this.d = d;
    }
    @Override
    public int getNumber() {
        return number;
    }
    @Override
    public void execute() {
        for(int i=start; i<end; i+=d){
            number = i;
            notifyObservers();
        }
    }
}

 

▷메인 역할 - Main.java

package main.java.designpattern.observer;

public class Main {
    public static void main(String[] args){
        NumberGenerator randGenerator = new RandomNumberGenerator();
        IncrementalNumberGenerator incrementalNumberGenerator = new IncrementalNumberGenerator(3,30,3);
        Observer digitObsvr = new DigitObserver();
        Observer grphObsvr = new GraphObserver();
        Observer oddObsvr = new OddObserver();

        randGenerator.addObserver(digitObsvr);
        randGenerator.addObserver(grphObsvr);
        randGenerator.addObserver(oddObsvr);
        randGenerator.execute();

        incrementalNumberGenerator.addObserver(digitObsvr);
        incrementalNumberGenerator.addObserver(grphObsvr);
        incrementalNumberGenerator.addObserver(oddObsvr);
        incrementalNumberGenerator.execute();
    }
}

 

---------------------------------------------------------
※참고자료
-Java언어로 배우는 디자인 패턴 입문
-Headfirst Design Pattern
-나무위키, 위키백과, 네이버백과
-리팩토링구루(https://refactoring.guru/ko)

반응형