05-15 20:52
Recent Posts
Recent Comments
관리 메뉴

miinsun

[디자인패턴/자바] 옵저버 패턴 Observer Pattern 본문

Language/Java

[디자인패턴/자바] 옵저버 패턴 Observer Pattern

miinsun 2022. 8. 16. 00:28

기상 스테이션 프로젝트

기상 스테이션은 온도, 습도, 기압의 정보를 구독 신청을 한 디바이스에 제공한다. 이때, 모든 디바이스는 매번 온도, 습도, 기압에 변화가 생길 때 마다 정보를 제공받을 수 있어야한다. 만약 고객이 해지를 원한다면 정보를 계속 받지 않을 수 있다. 


옵저버 패턴

위와 같은 기능은 옵저버 패턴의 기본 기능들로 이뤄져있다.

옵저버 패턴은
한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체에게 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다 의존성을 정의한다.
  • 옵저버 패턴에서는 정보를 제공하는 객체는 "주제"라고 하며, 정보를 제공받는 객체는 "옵저버"라 한다.
  • 옵저버 패턴은 일련의 객체 사이에서 일대다 관계를 정의한다. (1:주제, M:옵저버)
  • 옵저버는 주제에 딸려 있으며 주제의 상태가 바뀌면 옵저버에게 정보가 전달됩니다.
  • 옵저버 패턴은 느슨한 결합이다 -> 주제와 옵저버는 달라져도 서로 영향을 받지 않는다.
    • 느슨하게 결합하는 디자인을 사용하면 객체 사이의 상호의존성을 최소화할 수 있어 변경 사항이 생겨도 무난히 처리할 수 있는 유연한 객체지향 시스템을 구축 할 수 있다.
    • 주제는 옵저버의 구상 클래스가 무엇인지, 옵저버가 무엇을 하는지 알 필요가 없다.
    • 새로운 옵저버를 추가할 때도 주제를 변경할 필요가 전혀 없다.
    • 주제와 옵저버는 서로 독립적으로 재사용할 수 있습니다.

 


주제 '기상 스테이션' 구현하기

주제 인터페이스는 아래 코드와 같다. registerObsever로 옵저버를 등록하고 removeObserver로 옵저버를 삭제할 수 있다. notifyObserver는 주제의 상태가 변경 됐을 때 모든 옵저버에게 변경 된 내용을 알린다. 위에 소개된 메소드를 서브 클래스가 구현 할 수 있도록 인터페이스화 한다.

public interface Subject{
	public void registerObserver(Observer o);	// 옵저버를 등록
    public void removeObserver(Observer o);		// 옵저버를 삭제
    public void notifyObservers();	// 주제의 상태가 변경됐을 때, 모든 옵저버에게 내용을 알림
}

public interface Observer{
	public void update (float tmp, float humidity, float pressure);	// 기상 정보가 변경됐을 때, 옵저버에게 전달되는 값
}

public interface DisplayElement{
	public void display();
}

 

Weather Station을 코드로 구현하면 다음과 같다.

public class WeatherData implemetns Subject{
	private List<Observer> observers;
    private float temp;
    private float humidity;
    private float pressure;
    
    public WeatherData(){
    	observers = new ArrayList<Observer>();
    };
    
    public void registerObserver(Observer o){
    	observers.add(o);
    }
    
    public void removeObserver(Observer o){
    	observers.remove(o);
    }
    
    public void notifyObserver(){
    	for(Observer observer : observers){
        	observers.update(temp, humidity, pressure);
        }
    }
    
    public void measurementsChanged(){
    	notifyObservers();
    }
    
    public void setMeasurements(float temp, float humidity, float pressure){
    	this.temp = temp;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
}

 

 

고객사에 Observer 코드를 적용하면 

public class CurrentConditionsDisplay implements Observers, DisplayElements{
	private flaot temp;
    private flaot humidity;
    private WeatherData weatherData;
    
    public CurrentConditionsDisplay(WeatherData weatherData){
    	this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }
    
    public void update(float temp, float humidity, float pressure){
    	this.temp = temp;
        this.humidity = humidity;
        display();
    }
    
    public void display(){
    	System.out.println(){"현재 상태: 온도" + temp + "F, 습도 " + humidity() + "%"};
    }
}

 

테스트 코드는 아래와 같이 적용할 수 있다.

public class WeatherStation{
	public static void main(String[] args){
    	WeatherData weatherData = new WeatherData();
        
        Phone phone = new Phone(weatherData);
        Tab tab = new Tab(weatherData);
        
        weatherData.setMeasurements(80, 65, 30.4f);
        weatherData.setMeasurements(82, 70, 29.2f);
        weatherData.setMeasurements(78, 90, 29.2f);
    }
}

 

옵저버 방식은 푸시 방식과 풀 방식이 있는데 보통 옵저버가 필요한 데이터를 골라서 가져가도록 하는 방법이 더 좋게 작용합니다. 고객이 WeatherData 객체로부터 필요할 때마다 데이터를 끌어오면 코드를 일반화 하기 쉽고, 다른 고객을 추가히기도 더 쉽습니다. 주제가 자신의 데이터에 관한 getter메소드를 가진다면 필요한 데이터를 당겨올 때 해당 메소드를 호출할 수 있습니다.

참고로 JDK에 있는 자바빈과 스윙 라이브러리에도 옵저버 패턴을 쓰고 있다.이처럼 디자인 패턴을 배우면 라이브러리가 설계된 원리와 동기를 쉽고 빠르게 이해할 수있다는 장점이 있다. 만약 자바빈에 있는 옵저버 패턴이 궁금하다면 PropertyChangeListener 인터페이스를 확인합시다. 

Comments