ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Design Pattern] Observer Pattern
    Learn/Architecture 2022. 8. 27. 22:55

    # 개요 

    한 쪽의 오브젝트가 변화가 있을 때, 다른 오브젝트들이 노티아야 할 때 사용하는 패턴

     

    Observer Pattern은 아래와 같은 상황을 해결하기 위해 사용된다. 

    아래의 예시에서 WeatherData는 WeatherStation으로부터 날씨 데이터를 받아서 Device에 전달한다. 

    WeatherStation으로부터 날씨 변화를 받기 위해서는 아래 구조에서 measurementChanged 부분을 구현해야 한다. 

        > 날씨 데이터의 구성 요소가 변하는 것에도 대응할 수 있어야 하고 확장성도 고려해야 한다. 

    이를 위해 아래와 같이 update함수를 고려해 볼 수 있다. 

    하지만 변수가 늘어나거나 Display에 변경이 있을 때 마다 매번 코드를 수정해줘야 한다. 

    public class WeatherData {
    
    	// instance variable declarations
    	
    	public void measurementsChanged() {
    
    		float temp = getTemperature();
    		float humidity = getHumidity();
    		float pressure = getPressure();
    
    		// 아래 부분이 변동성이 높음
    		currentConditionDisplay.update(temp, humidity, pressure);
    		statisticsDisplay.update(temp, humidity, pressure);
    		forecastDisplay.update(temp, humidity, pressure);
    	}
        
    	// other WeatherData methods here
    }

    # Observer Pattern

    publish / subscribe 모델이라고도 부른다. 

     

    신문사과 신문구독자로 생각하면 이해하기 쉽다. 

        - 신문사는 구독자에게 새로운 신문이 나올때마다 알려준다. 

        - 구독을 해지하면 더이상 알려주지 않는다. 

     

    클래스 다이어그램으로 표현해보면 다음과 같다. 

    - Subject (publisher)쪽은 구독신청, 해지, 알림에 대한 API를 제공한다. (ConcreteSubject에서 구현)

    - Observer쪽은 update API만 제공하면 된다. (ConcreteObserver에서 구현)

    - ConcreteSubject는 ConcreteObserver쪽에 대한 지식이 전혀 없다. (Interface로만 통신하므로)

    Observer Pattern은 잘 쓰면 커플링을 줄여준다. 

     

    # 구현

    Interface

    public interface Subject {
    	public void registerObserver(Observer o);
    	public void removeObserver(Observer o);
    	public void notifyObservers();
    }
    
    public interface Observer {
    	public void update(float temp, float humidity, float pressure);
    }
    
    public interface DisplayElement {
    	public void display();
    }

    ConcreteSubject

    - setMeasurements는 테스트를 쉽게 하기위한 helper method

    public class WeatherData implements Subject {
    	private ArrayList observers;
    	private float temperature;
    	private float humidity;
    	private float pressure;
        
    	public WeatherData() {
    		observers = new ArrayList();
    	}
    
    	public void registerObserver(Observer o) {
    		observers.add(o);
    	}
    
    	public void removeObserver(Observer o) {
    		int i = observers.indexof(o);
    		if(i>=0) observers.remove(i);
    	}
        
    	public void notifyObservers() {
    		for(int i = 0; i < observers.size(); i++) {
    		Observer observer = (Observer)observers.get(i);
    		observer.update(temperature, humidity, pressure);
    		}
    	}
    
    	public void measurementsChanged() {
    		notifyObservers();
    	}
        
        // helper method
    	public void setMeasurements(float temperature, float humidity, float pressure) {
    		this.temperature = temperature;
    		this.humidity = humidity;
    		this.pressure = pressure;
    		measurementsChanged();
    	}
    // other WeathreData methods here
    }

    ConcreteObserver

    public class CurrentConditionDisplay implements Observer, DisplayElement {
    	private float temperature;
    	private float humidity;
    	private Subject weatherData;
    	
        public CurrentConditionDisplay(Subject weatherData) {
    		this.weathreData = weatherData;
    		weatherData.registerObserver(this);
    	}
    
    	public void update(float temperature, float humidity, float pressure) {
    		this.temperature = temperature;
    		this.humidity = humidity;
    		display();
    	}
        
    	public void display() {
    		System.out.println(“Current conditions: ” + temperature + “F degrees and” + humidity + “% humidity”);
    	}
    }

    # Observable (JAVA)

    자바에는 이미 Observable이라는 클래스에 구독, 해지, 노티같은 것들이 이미 구현되어있다. 

    Interface도 이미 구현되어있다. 

    Publisher는 노티를 보내기전에 Setchanged 함수를 호출해야 한다. 

      - 변화가 있을 때마다 노티를 보내면 오버헤드가 크기때문에 이렇게 설계했다. (git의 commit느낌?)

     

    Subscriber는 Observer interface를 구현해야 한다. 

      - 관심있는 데이터를 publisher의 API로 호출하는 방식 (pull)

    import java.util.Observable;
    import java.util.Observer;
    
    public class CurrentConditionsDisplay implements Observer, DisplayElement {
    	Observable observable;
    	private float temperature;
    	private float humidity;
    	
        public CurrentConditionsDisplay(Observable observable) {
    		this.observable = observerble;
    		observable.addObserver(this);
    	}
    
    	public void update(Observable obs, Object arg) {
    		if(obs instanceof WeatherData) {
    			WeatherData weatherData = (WeatherData)obs;
    			this.temperature = weatherData.getTemperature();
    			this.humidity = weatherData.getHumidity();
    			display();
    		}
    	}
    
    	public void display() {
    		System.out.println(“Current conditions: ” + temperature + “F degrees and” + humidity + “% humidity”);
    	}
    }

    # QUIZ

    Q1. Observer 패턴에서 약한 커플링 (loose coupling)이 어떻게 달성되는지 간략히 설명하시오. 

    A. 커뮤니케이션에 참여하는 두 객체는 인터페이스로만 소통하므로 서로간에 아는 정보가 적음. 

     

    Q2. Observer 패턴을 사용할 때, Publisher 역할을 수행하는 객체가 여러 개 있을 수 있는가?

    A. Subscriber들이 pull 방식으로 구독신청을 하는 방식이므로 가능하다.  또한 publisher이면서 동시에 subscriber일 수도 있다. 

    'Learn > Architecture' 카테고리의 다른 글

    [Design Pattern] Iteration Pattern  (0) 2022.08.28
    [Design Pattern] Template Method Pattern  (0) 2022.08.28
    [Design Pattern] Strategy Pattern  (0) 2022.08.26
    [Design Pattern] GRASP Principle  (0) 2022.08.24
    [Design Pattern] SOLID Principle  (0) 2022.08.21

    댓글

Designed by Tistory.