해당 포스팅은 한빛 미디어 헤드퍼스트 디자인 패턴(에릭 프리먼, 엘리자베스 롭슨 저)를 통해 공부한 내용을 정리한 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
좋은 프로그래머는 자기 두뇌를 사용한다. 그러나 좋은 가이드라인은 모든 케이스를 고려해야만 하는 노력을 줄여준다.
(Francis Glassborow, 개발자)
Observer 패턴 이해하기
신문사 + 구독자 = Observer 패턴(publish-subscribe 패턴과 다름)
- 구성요소
- 신문사 => 주제(Subject)
- 구독자 => 옵저버(Observer)
- 주제에서는 중요한 데이터를 관리
- 주제의 데이터가 바뀌면 옵저버에게 이벤트(데이터가 바뀌었다는 소식)를 전달.
- 주제를 구독하고 있는 옵저버에게 소식이 전달되었기에 전달받은 내용을 옵저버는 갱신.(최신 데이터 유지)
작동원리
- 주제는 옵저버들에게 데이터를 전달.
- 어떤 객체가 주제의 데이터를 전달받고 싶어하면 주제를 구독하면 됨.(옵저버가 되는 형태)
- 주제의 데이터를 그만 업데이트하고 싶으면, 주제의 구독을 해지.
Observer 패턴
객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체에게 연락이 가고 자동적으로 내용이 갱신되는 방식.
일대다(one-to-many) 의존성을 정의(일: Subject, 다: Observer)
느슨한 결합
- 한 객체가 다른 객체에 너무 의존적이면 단단한 결합이라 함.
느슨한 결합
- 객체가 부서질 가능성이 낮음
- 객체의 세세한 부분까지 알 필요가 없음
- 상호작용할 수 있지만 잘 모르는 관계
- 다른 객체와 상관없이 설계하면 변화에 더 잘 대응할 수 있는 디자인을 만들 수 있음 => 유연한 디자인
옵저버 패턴에 적용한 내용
- 주제는 옵저버가 특정 인터페이스(Observer interface)를 구현한다는 사실만 알고 있음.
- 옵저버는 언제든 추가/제거 가능
- 새로운 형식의 옵저버를 추가해도 주제 변경이 필요가 없음.
- 주제와 옵저버는 서로 독립적으로 재사용 가능.
- 주제나 옵저버가 달라져도 서로에게 영향을 미치지 않음.
💡 상호작용하는 객체 사이에는 느슨한 결합을 사용해야함.
풀 방식으로 코드 바꾸기
- 이전까지의 방식
- 주제가 옵저버에게 데이터를 보내는 푸시(PUSH)
- 현재 사용할 방식
- 옵저버가 주제로부터 데이터를 당겨오는 풀(PULL)
코드
/*
* observer interface
*/
public interface Observer {
public void update(float temp, float humidity, float pressure);
public void update();
}
/*
* display interface
*/
public interface DisplayElement {
public void display();
}
public class CurrentConditionsDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private WeatherData weatherData;
public CurrentConditionsDisplay(WeatherData weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
// PUSH 방식
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
display();
}
// PULL 방식
public void update() {
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
display();
}
public void display() {
System.out.println("CurrentConditions Display");
System.out.println("temperature = " + temperature + " humidity = " + humidity);
}
}
/*
* subject interface
*/
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer observer);
public void notifyObservers();
}
public class WeatherData implements Subject {
// 인스턴스 변수 선언
private List<Observer> observers;
private float temperature;
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 notifyObservers() {
// Push방식
// Observer들에게 갱신 값 보내주기.
for (Observer o : observers) {
o.update(temperature, humidity, pressure);
}
// Pull
// Observer에서 값 가져오기
for (Observer o : observers) {
o.update();
}
}
public void measurementsChanged() {
notifyObservers();
/*
* 현재 구조에서는 display들이 update 메소드를 요청하는데 하나로 묶을 수 있을 것 같음.
* 최신 측정 값을 가져오는 시점을 판단하기 어려움
* Observer 패턴 도입.
*/
}
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged(); // 최신 측정값 Observer들에게 갱신 값 보내주기.
}
}
'기타 > 디자인패턴' 카테고리의 다른 글
[Design Pattern] Command Pattern (2) | 2024.06.03 |
---|---|
[Design Pattern] Singleton Pattern (0) | 2024.05.31 |
[Design Pattern] Factory Pattern (0) | 2024.05.29 |
[Design Pattern] Decorator Pattern (0) | 2024.05.28 |
[Design Pattern] Strategy Pattern (0) | 2024.05.27 |