mojo's Blog
Observer Pattern 본문
Observer Pattern
목적: 시스템 내 다른 객체의 상태 변경을 하나 이상의 객체에 알리기 위함
사용 시기
- 커뮤니케이션을 위해 느슨한 커플링이 필요함
- 하나 이상의 객체에서 상태 변경이 다른 객체에서 동작을 트리거링 필요
- 브로드캐스팅 기능이 필요함
※ Application Overview
현재 상태 디스플레이는 위와 같이 사용자는 몬스터의 체력, 위치 정보를 시각화하여 볼 수 있다.
다른 디스플레이는 몬스터의 체력, 위치 정보를 수치화하여 볼 수 있고,
또 다른 디스플레이는 몬스터의 체력, 위치 정보를 백분율로 볼 수 있다.
MonsterData 객체를 이용하여 어플리케이션을 만들어 위와 같이 3 개의 display 를 업데이트 해야 한다.
몬스터 데이터를 사용하는 요소들은 아래와 같다.
- 현재 몬스터 체력 표시
- 현재 몬스터 위치 표시
MonsterData 가 새로운 측정값을 가질 때마다 업데이트해야 한다.
시스템을 확장할 수 있어야 한다.
- 개발자는 새로운 사용자 정의 디스플레이 요소를 만들 수 있어야 함
- 사용자는 원하는 만큼 어플리케이션에 디스플레이 요소를 추가하거나 제거할 수 있어야 함
※ measurementsChanged 에 대한 구현
public class MonsterData {
// instance variable declarations
public void measurementsChanged() {
int stamina = getStamina();
Point location = getLocation();
currentConditionDisplay.update(stamina, location);
digitizationDisplay.update(stamina, location);
percentageDisplay.update(stamina, location);
}
// other MonsterData methods here
}
위와 같이 구체적인 구현에 대한 코딩을 함으로 인해서,
프로그램을 변경하지 않고 다른 display 요소를 추가하거나 제거할 방법이 없다.
※ The Observer Pattern Defined
객체 간의 일대일 종속성을 정의하여 하나의 객체가 상태를 변경할 때,
해당 객체의 종속성을 모두 자동으로 알리고 업데이트 한다.
Observers는 의존성 객체들로 데이터가 변경될 때 업데이트할 Subject에 의존한다.
Observer Pattern에 대한 클래스 다이어그램은 위와 같다.
대략적으로 Subject는 NotifyObservers 메서드를 통해 변경된 데이터를 Observer 에게 알리는 구조이다.
※ The Power of Loose Coupling
상호 작용하는 객체간 느슨하게 결합된 설계를 위해 노력해야 한다.
두 객체가 느슨하게 결합되면 상호 작용할 수 있지만, 서로에 대한 정보가 거의 없다.
Observer pattern은 subject와 observers가 느슨하게 결합된 객체 설계를 제공한다.
Observer Pattern에 대한 클래스 다이어그램을 참고하여,
Monster Station 에 대한 설계를 하면 다음과 같다.
※ Interface 코드
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
}
public interface Observer {
public void update(int stamina, Point location);
}
public interface DisplayElement {
public void display();
}
※ Subject 를 구현한 MonsterData 코드
public class MonsterData implements Subject {
private ArrayList observers;
private int stamina;
private Point location;
public MonsterData() {
observers = new ArrayList();
}
public void registerObserver(Observer o) {
observers.add(o);
}
public void removeObserver(Observer o) {
int idx = observers.indexof(o);
if (idx >= 0)
observers.remove(idx);
}
public void notifyObservers() {
for (int i = 0; i < observers.size(); i++) {
Observer observer = (Observer)observers.get(i);
observer.update(stamina, location);
}
}
public void measurementsChanged() {
notifyObservers();
}
public void setMeasurements(int stamina, Point location) {
this.stamina = stamina;
this.location = location;
measurementsChanged();
}
// other MonsterData methods here
}
몬스터를 디바이스를 통해 보고 있는 유저들은 ArrayList인 observers 을 통해 위와 같이 관리한다.
몬스터의 체력이나 위치가 변경될 경우, 값을 업데이트하면서 유저들에게 체력, 위치를 업데이트 시켜서
디바이스를 통해 보고 있는 몬스터의 체력이나 위치가 조정되어 보여질 수 있도록 한다.
※ Observer, DisplayElement 를 구현한 CurrentConditionDisplay 코드
public class CurrentConditionDisplay implements Observer, DisplayElement {
private int stamina;
private Point location;
private Subject monsterData;
public CurrentConditionDisplay(Subject monsterData) {
this.monsterData = monsterData;
monsterData.registerObserver(this);
}
public void update(int stamina, Point location) {
this.stamina = stamina;
this.location = location;
display();
}
public void display() {
// display current condition in device
}
}
디바이스를 통해 현재 몬스터의 체력, 위치를 볼 수 있는 클래스이다.
Subject인 monsterData에 자기 자신의 객체를 observer로 등록함으로써,
체력이나 위치가 변경될 때마다 update 가 호출되어 새로운 상태의 몬스터가 디바이스에 보여지게 된다.
※ Monster Station 에 대한 코드
public class MonsterStation {
public static void main(String[] args) {
MonsterData monsterData = new MonsterData();
CurrentConditionDisplay currentDisplay =
new CurrentConditionDisplay(monsterData);
DigitizationDisplay digitizationDisplay =
new DigitizationDisplay(monsterData);
PercentageDisplay percentageDisplay =
new PercentageDisplay(monsterData);
monsterData.setMeasurements(100, new Point(100, 100));
monsterData.setMeasurements(90, new Point(120, 100));
monsterData.setMeasurements(70, new Point(120, 140));
}
}
위 코드를 정리하면,
(1) 디바이스를 통해 몬스터를 나타내는 currentDisplay
(2) 몬스터의 체력, 위치 값을 나타내는 digitizationDisplay
(3) 몬스터의 체력, 위치를 백분율로 나타내는 percentDisplay
위 3개의 display가 각각 subject인 monsterData를 참조하는 상태가 된다.
체력, 위치가 변경될 때마다 이뤄지는 작업은 다음과 같다.
(1) 현재 monster의 체력, 위치를 업데이트
(2) 3 개의 observer에 update 메서드 호출
(3) observer가 가지고 있는 monster의 체력, 위치를 새로 업데이트 해주고 display 메서드 호출
※ Loose coupling 달성
- 두 객체가 서로에 대해 거의 알지 못하지만, 상호 작용할 수 있는 경우 느슨하게 결합된 것으로 간주
- Observer pattern은 loose coupling의 좋은 예시임
- Subject가 observer에 대해 아는 것은 특정 인터페이스를 구현한다는 것 뿐임
- 이는 subject 또는 observer 중 한 명의 변경 사항이 다른 사람에게 영향을 미치지 않기 때문에 좋음
- Subject를 변경하지 않으면서 언제든 새로운 유형의 observer를 추가할 수 있음
'Design Patterns' 카테고리의 다른 글
State Pattern (0) | 2024.06.06 |
---|---|
Template Method Pattern (0) | 2024.06.06 |
Strategy Pattern (0) | 2024.06.06 |
GRASP (1) | 2024.05.15 |
SOLID 원칙 (0) | 2024.05.06 |