[2장] Observer Pattern

ss0510s·2022년 7월 10일
0

Observer Pattern

한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체에게 연락이 가고 자동으로 내용이 갱신되는 방식으로, 일대다 의존성을 정의한다.

  • Subject에서 중요한 데이터를 관리하고, Subject의 상태가 변화하면 Observer에서 소식이 전해진다.
  • Observer는 Subject를 구독하고 있으며 갱신 내용을 전달 받는다.

OO 원칙
상호작용하는 객체 사이에는 가능하면 느슨한 결합을 사용해야 한다.

느슨한 결합
객체 사이의 상호의존성을 최소화한다.

  • Subject는 Observer가 특정 인터페이스를 구현한다는 사실만 안다.
  • 언제든지 Observer를 추가 가능하다.
  • 새로운 형식의 Observer를 추가할 때마다 Subject를 수정할 필요가 없다.
  • Subject와 Observer는 서로 독립적으로 재사용 가능하다.
  • Subject와 Observer는 달라져도 서로 영향을 미치지 않는다.

기상 스테이션

  • 기존 코드
public class WeatherData {
	// 인스턴스 변수 선언
    public void measurementChanged() {
    	float temp = getTemperature();
        float humidity = getHumidity();
        float pressure = getPressure();
        
        currentConditionDisplay.update(temp, humidity, pressure);
        statisticsDisplay.update(temp, humidity, pressure);
        forecastConditionDisplay.update(temp, humidity, pressure);
    }
}

문제점 : 새로운 기상 측정 데이터가 들어올 때마다 디스플레이를 업데이트해야한다.

코드 구현

  1. 바뀌는 부분: Subject의 상태, Observer의 개수, 형식
  2. 추가되는 Display를 Observer로 하고, 이를 등록, 삭제, 소식 전달을 할 Subject 클래스를 생성한다.
  3. Observer마다 바뀌는 Display 요소를 캡슐화한다.
  4. Composition 관계

Subject와 Observer 인터페이스 생성

  • Subject 인터페이스는 Observer을 등록, 삭제하는 메서드가 있다.
  • Objserver 인터페이스는 Subject의 상태가 바뀌었을 때 호출되는 update 메서드가 존재한다.
public interface Subject{
	//Observer를 인자로 받아 등록, 삭제
	public void registerObserver(Observer o);
	public void removeObserver(Observer o);
    // Observer에게 상태 변화를 알림
	public void notifyObserver(Observer o);
}

public interface Observer{
	// Subject로 부터 상태변화를 받음
	public void update();
}

public interface Display{
	public void display();
}

Concrete Subject 생성

  • Subject 인터페이스를 구현한다.
  • Observer들을 저장하고, 업데이트 된 정보를 Observer에게 알린다.
public class WeatherData implements Subject {
	private List<Observer> observers; // observer 객체를 저장할 리스트
    private float temperature;
    private float humidity;
    private float pressure;
    
    public WeatherData() {
    	observers = new ArrayList<Observer>();
   	}
    public void registerObserver(Observer o) {
		observers.add(o); // observer 등록
	}
    public void removeObserver(Observer o) {
		observers.remove(o); // observer 제거
	}
	
	public void notifyObservers() {
    	// 모든 observer에게 상태 변화를 알려줌
		for (Observer observer : observers) {
			observer.update();
		}
	}
	
	public void measurementsChanged() {
    	// observer에게 값 전달
		notifyObservers();
	}
	
    // 값 갱신
	public void setMeasurements(float temperature, float humidity, float pressure) {
		this.temperature = temparture;
		this.humidity = humidity;
		this.pressure = pressure;
		measurementsChanged();
	}
	//변수 get 메서드
	public float getTemperature() {
		return temperature;
	}
	
	public float getHumidity() {
		return humidity;
	}
	
	public float getPressure() {
		return pressure;
	}

}

Concrete Observer 생성

  • Subject에 등록할 Observer을 생성한다.
public class CurrentConditionsDisplay implements Observer, DisplayElement {
	private float temperature;
	private float humidity;
	private WeatherData weatherData;
	
	public CurrentConditionsDisplay(WeatherData weatherData) {
		this.weatherData = weatherData; // 구독할 Subject를 받음
		weatherData.registerObserver(this); // Observer로 등록
	}
	
	public void update() {
    	// Subject로부터 갱신된 정보 중 필요한 정보만을 받음
		this.temperature = weatherData.getTemperature();
		this.humidity = weatherData.getHumidity();
		display();
	}
	
	public void display() {
		System.out.println("Current conditions: " + temperature 
			+ "C degrees and " + humidity + "% humidity");
	}
}

	...

Test.java

public class WeatherStation {

	public static void main(String[] args) {
		// subject 생성
		WeatherData weatherData = new WeatherData();

		// observer 등록
		CurrentConditionsDisplay currentDisplay = 
			new CurrentConditionsDisplay(weatherData);
		StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
		ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);

		// 값 변경 후 모든 observer 에게 전달
		weatherData.setMeasurements(80, 65, 30.4f);
		weatherData.setMeasurements(82, 70, 29.2f);
		weatherData.setMeasurements(78, 90, 29.2f);

		// observer 해지
		weatherData.removeObserver(forecastDisplay);
		weatherData.setMeasurements(62, 90, 28.1f);
	}
}

적용

Swing 라이브러리

  • Swing: 사용자 인터페이스 용도의 자바 GUI 툴킷
  • 다양한 유형의 이벤트를 감시하는 옵저버(리스너)를 추가하거나 제거하는 메서드들이 존재한다.
  • ex) JButton: 슈퍼클래스인 AbstractButton에는 리스너를 추가하고 제거하는 메서드가 존재한다.
    ActionListener: 버튼 이벤트를 감시하고, 이벤트 변화가 발생했을 때 적용할 기능을 구현할 수 있다.

MVC 패턴

  • Model(주체)에서 변경 사항이 생겨 update() 메서드로 옵저버인 View에게 알려주고 이를 기반으로 Controller 등이 동작한다.

proxy 객체

  • proxy 객체: 어떤 대상의 기본적인 동작의 작업을 가로챌 수 있는 객체
  • target(프록시할 대상), handler(프록시 객체의 target 동작을 가로채서 정의할 동작들이 정해져 있는 함수)
  • 접근을 가로채서 상태 변화를 감시한다.

Vue.js 3.0

  • ref나 reactive로 정의하면 값 변경시 자동으로 DOM(웹 브라우저 화면을 이루고 있는 요소)에 있는 값이 변경된다.
  • ref는 변수를 반응성 데이터로 만든다. ref로 선언된 변수의 값이 변경되면 해당 값을 사용하는 Vue 컴포넌트의 뷰도 자동으로 업데이트된다.
  • proxy 객체의 옵저버패턴을 이용하여 구현한 것이다.
profile
개발자가 되기 위해 성장하는 중입니다.

0개의 댓글