배경
날씨 데이터를 디스플레이 장비에 표시하는 애플리케이션 개발 요청입니다.
요구사항
WeatherData객체를 통해 온도, 습도, 기압 3가지 디스플레이 항목을 표시해야 합니다.WeatherData는 실시간으로 갱신됩니다.
- 데이터는 디스플레이 장비에 표시되어야 합니다.
- 디스플레이 항목은 개발자가 손쉽게 추가할 수 있어야 합니다.
패턴 미적용
public class WeatherData {
private double temperature;
private double humidity;
private double pressure;
public void measurementsChanged() {
double temp = getTemperature();
double humidity = getHumidity();
double pressure = getPressure();
CurrentConditionsDisplay ccd = new CurrentConditionsDisplay();
ccd.update(temp, humidity, pressure);
StatisticsDisplay sd = new StatisticsDisplay();
sd.update(temp, humidity, pressure);
ForecastDisplay fd = new ForecastDisplay();
fd.update(temp, humidity, pressure);
}
}
디자인 원칙으로 문제점 찾기
- 변경되지 않는 부분과 변경되는 부분을 구분하고, 변경되는 부분을 찾아 캡슐화해야 합니다.
- 요구사항에 따라 Display는 추가가 가능해야 하므로 Display의 개수는 변경될 예정입니다.
- Display는
WeatherData의 업데이트된 데이터를update()로 호출하여 반영하도록 공통됩니다.- 어떤 정보를 받아볼지는 Display마다 다를 수 있습니다.
- Display는
WeatherData에서 받은 정보를 표시합니다.
- 구현은 인터페이스에 맞춰 프로그래밍하는 것이 바람직합니다.
- 현재는
CurrentConditionsDisplay와 같은 구체 클래스에 의존하고 있습니다.
- 현재는
리팩토링 계획
WeatherData관련- 변경된 기상 정보를 제공할 Display 목록을 관리해야 합니다(추가, 삭제).
- 기상 정보가 변경될 때마다
WeatherData에서 Display에 전달할 수 있어야 합니다(Display의update호출이 필요합니다). - → 리스닝하는 Display를 관리하는 객체로 추상화합니다.
- (추가) 추후 다른 데이터도 제공할 수 있도록, Display처럼 최신 정보를 지속적으로 제공해야 하는 객체들을 다루는 인터페이스로 추상화합니다.
- 기상 정보가 변경될 때마다
- 변경된 기상 정보를 제공할 Display 목록을 관리해야 합니다(추가, 삭제).
- Display 관련
- Display들은
WeatherData에서 제공하는 변경된 정보를 반영하는update()메서드를 포함해야 합니다.- 어떤 정보를 받아볼지는 Display마다 다를 수도 있고 같을 수도 있습니다.
- → 구현체 클래스에서 적절히 Override합니다.
- Display들은
WeatherData에서 제공받은 정보를 표시하는display()메서드를 구현해야 합니다.- 어떤 정보를 표시할지는 Display마다 다를 수도 있고 같을 수도 있습니다.
- → 구현체 클래스에서 적절히 Override합니다.
- 즉, 변경된 정보를 반영하는 인터페이스와 어떤 정보를 표시하는지에 대한 두 개의 인터페이스가 필요합니다.
- Display들은
위와 같이 주기적으로 특정 정보를 받아봐야 하는 상황에 사용할 수 있는 패턴이 옵저버 패턴입니다.
옵저버 패턴의 정의
한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들에게 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다(one-to-many) 의존성을 정의합니다.
신문사와 구독자의 관계로 비유할 수 있습니다. 이때 신문사를 Subject, 구독자를 Observer라고 볼 수 있습니다.
옵저버 패턴을 아래의 디자인 원칙을 추가하여 적용해보겠습니다.
발행-구독 패턴(pub/sub pattern)과 같은 개념인지에 대한 질문 → 다릅니다
- pub/sub은 1:N이 아니라 N:M 관계입니다.
- Subject처럼 Observer를 직접 관리하지 않습니다.
- 중간 관리자(메시지 브로커)가 존재합니다.
- 서로 알 필요가 없어 더 유연합니다.
- 대부분 비동기식(이벤트 큐, 메시지 큐 기반)으로 동작합니다.
디자인 원칙(느슨한 결합)
디자인 원칙
상호작용하는 객체 사이에는 가능하면 느슨한 결합(Loose coupling)을 사용해야 합니다.
- 느슨한 결합은 객체들이 상호작용할 수는 있지만, 서로를 잘 모르는 관계를 의미합니다.
- 느슨한 결합을 사용하면 유연성이 높아집니다.
- Subject는 자신의 데이터를 받는 객체들이 Observer 인터페이스를 구현했다는 사실만 알고 있습니다.
- 따라서 Observer가 아무리 많아져도 신경 쓸 필요가 없으므로 확장에 용이합니다.
- Observer가 변경되어도 Subject를 수정할 필요가 없습니다.
- Subject와 Observer는 서로 독립적으로 재사용 가능합니다.
- Observer에서
update만 제대로 구현되어 있다면 어떻게 수정되어도 Subject에는 영향을 주지 않습니다. - Subject를 어떻게 수정하더라도 Observer의
update만 제대로 호출되면 됩니다.
디자인 패턴을 적용한 리팩토링
WeatherData 설계
Subject인터페이스를 만들어 구현합니다.
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
Display들(Observer) 설계
Observer,DisplayElement인터페이스를 생성하고 구현합니다.
public interface Observer {
void update(double temperature, double humidity, double pressure);
}
public interface DisplayElement {
void display();
}
자세한 코드는 Github 을 참고하시기 바랍니다.