๐ ์ต์ ๋ฒ(Observer) ํจํด - ์๋ฒฝ ๋ง์คํฐํ๊ธฐ
Observer Pattern
์ต์ ๋ฒ ํจํด(Observer Pattern)์ ์ต์ ๋ฒ(๊ด์ฐฐ์)๋ค์ด ๊ด์ฐฐํ๊ณ ์๋ ๋์์์ ์ํ๊ฐ ๋ณํ๊ฐ ์์ ๋๋ง๋ค ๋์์๋ ์ง์ ๋ชฉ๋ก์ ๊ฐ ๊ด์ฐฐ์๋ค์๊ฒ ํต์งํ๊ณ , ๊ด์ฐฐ์๋ค์ ์๋ฆผ์ ๋ฐ์ ์กฐ์น๋ฅผ ์ทจํ๋ ํ๋ ํจํด์ด๋ค.
์ต์ ๋ฒ ํจํด์ ์ฌํ ๋ค๋ฅธ ๋์์ธ ํจํด๋ค๊ณผ ๋ค๋ฅด๊ฒ ์ผ๋๋ค(one-to-many) ์์กด์ฑ์ ๊ฐ์ง๋๋ฐ, ์ฃผ๋ก ๋ถ์ฐ ์ด๋ฒคํธ ํธ๋ค๋ง ์์คํ ์ ๊ตฌํํ๋ ๋ฐ ์ฌ์ฉ๋๋ค. Pub/Sub(๋ฐํ/๊ตฌ๋ ) ๋ชจ๋ธ๋ก๋ ์๋ ค์ ธ ์๊ธฐ๋ ํ๋ค.
์ด ํจํด์ ์ดํดํ๋๋ฐ ์์ด ๋ด์ค ํผ๋๋ ์ ํ๋ธ๋ก ๋น์ ํด๋ณด๋ฉด ์ฝ๋ค. ์ ํ๋ธ ์ฑ๋์ ๋ฐํ์(Subject)๊ฐ ๋๊ณ ๊ตฌ๋ ์๋ค์ ๊ด์ฐฐ์(Observer)๊ฐ ๋๋ ๊ตฌ์กฐ๋ก ๋ณด๋ฉด ๋๋ค. ์ค์ ๋ก ์ ํ๋ฒ๊ฐ ์์์ ์ฌ๋ฆฌ๋ฉด ์ฌ๋ฌ๋ช ์ ๊ตฌ๋ ์๋ค์ ๋ชจ๋ ์์์ด ์ฌ๋ผ์๋ค๋ ์๋ฆผ์ ๋ฐ๋๋ฐ, ์ด๋ฅผ ํจํด ๊ตฌ์กฐ๋ก ๋ณด์๋ฉด ๊ตฌ๋ ์๋ค์ ํด๋น ์ฑ๋์ ๊ตฌ๋ ํจ์ผ๋ก์จ ์ฑ๋์ ์ด๋ ํ ๋ณํ(์์์ ์ฌ๋ฆฌ๊ฑฐ๋ ์ปค๋ฎค๋ํฐ์ ๊ธ์ ์ฐ๊ฑฐ๋)๊ฐ ์๊ธฐ๊ฒ ๋๋ฉด ๋ฐ๋ก ์ฐ๋ฝ์ ๋ฐ์ ํ์งํ๋ ๊ฒ์ด๋ค. ๋ฐ๋ฉด ๊ตฌ๋ ์ ํด์งํ๊ฑฐ๋ ์ํ ์์ฒญ์์๊ฒ๋ ์๋ฆผ์ด ๊ฐ์ง์๊ฒ ๋๋ค.
ํ๋ก๊ทธ๋๋ฐ์ ์ผ๋ก ์ต์ ๋ฒ ํจํด์ ์ฌ์ค '๊ด์ฐฐ' ํ๊ธฐ ๋ณด๋จ ๊ฐฑ์ ์ ์ํ ํํธ ์ ๋ณด๋ฅผ '์ ๋ฌ' ๋ฐ๊ธธ ๊ธฐ๋ค๋ฆฐ๋ค๊ณ ๋ณด๋ ๊ฒ์ด ์ ์ ํ๋ค. ๊ด์ฐฐ์๋ผ๋ ๋จ์ด ๋์์ค์์ ๋ฅ๋์ ์ผ๋ก ๋์์ ๊ด์ฐฐํ๋ ๊ฒ์ฒ๋ผ ๋๊ปด์ง์ง๋ง, ์ฌ์ค ๋์ ๊ฐ์ฒด๋ก๋ถํฐ ์๋์ ์ผ๋ก ์ ๋ฌ ๋ฐ๊ธฐ๋ฅผ ๊ธฐ๋ค๋ฆฌ๊ณ ์๊ธฐ ๋๋ฌธ์ด๋ค.
์ต์ ๋ฒ ํจํด ๊ตฌ์กฐ
- ISubject : ๊ด์ฐฐ ๋์์๋ฅผ ์ ์ํ๋ ์ธํฐํ์ด์ค
- ConcreteSubject : ๊ด์ฐฐ ๋นํ๋ ๋์์ / ๋ฐํ์ / ๊ฒ์์
- Observer๋ค์ ๋ฆฌ์คํธ(List, Map, Set ..๋ฑ)๋ก ๋ชจ์ ํฉ์ฑ(compositoin)ํ์ฌ ๊ฐ์ง๊ณ ์์
- Subject์ ์ญํ ์ ๊ด์ฐฐ์์ธ Observer๋ค์ ๋ด๋ถ ๋ฆฌ์คํธ์ ๋ฑ๋ก/์ญ์ ํ๋ ์ธํ๋ผ๋ฅผ ๊ฐ๊ณ ์๋ค. (register, remove)
- Subject๊ฐ ์ํ๋ฅผ ๋ณ๊ฒฝํ๊ฑฐ๋ ์ด๋ค ๋์์ ์คํํ ๋, Observer ๋ค์๊ฒ ์ด๋ฒคํธ ์๋ฆผ(notify)์ ๋ฐํํ๋ค.
- IObserver : ๊ตฌ๋ ์๋ค์ ๋ฌถ๋ ์ธํฐํ์ด์ค (๋คํ์ฑ)
- Observer : ๊ด์ฐฐ์ / ๊ตฌ๋
์ / ์๋ฆผ ์์ ์.
- Observer๋ค์ Subject๊ฐ ๋ฐํํ ์๋ฆผ์ ๋ํด ํ์ฌ ์ํ๋ฅผ ์ทจ๋ํ๋ค.
- Subject์ ์ ๋ฐ์ดํธ์ ๋ํด ์ ํ ์ ๋ณด๋ฅผ ์ฒ๋ฆฌํ๋ค.
์ต์ ๋ฒ ํจํด์ ์ฌํ ๋ค๋ฅธ ๋์์ธ ํจํด๊ณผ ๋๊ฐ์ด ์ํธ์์ฉํ ๊ฐ์ฒด๋ฅผ ํฉ์ฑ(compositoin)์ ํ๊ณ ๋ฉ์๋ ์์์ ํตํด ๊ตฌ์ฑํ๋ ์ฝ๋ ํจํด์์ ๋๊ฐ์ง๋ง, ํต์ฌ์ ํฉ์ฑํ ๊ฐ์ฒด๋ฅผ ๋ฆฌ์คํธ๋ก ๊ด๋ฆฌํ๊ณ ๋ฆฌ์คํธ์ ์๋ ๊ด์ฐฐ์ ๊ฐ์ฒด๋ค์๊ฒ ๋ชจ๋ ๋ฉ์๋ ์์์ ํตํ ์ ํ ํ์๋ฅผ ํ๋ค๋ ์ ์ ๊ธฐ์ตํ๋ฉด ๋๋ค.
์ต์ ๋ฒ ํจํด ํ๋ฆ
- ์ต์ ๋ฒ ํจํด์์๋ ํ๊ฐ์ ๊ด์ฐฐ ๋์์(Subject)์ ์ฌ๋ฌ๊ฐ์ ๊ด์ฐฐ์(Observer A, B, C)๋ก ์ผ ๋ ๋ค ๊ด๊ณ๋ก ๊ตฌ์ฑ๋์ด ์๋ค.
- Observer ํจํด์์๋ ๊ด์ฐฐ ๋์ Subject์ ์ํ๊ฐ ๋ฐ๋๋ฉด ๋ณ๊ฒฝ์ฌํญ์ ์ต์ ๋ฒ ํํ ํต๋ณดํด์ค๋ค. (notifyObserver)
- ๋์์๋ก๋ถํฐ ํต๋ณด๋ฅผ ๋ฐ์ Observer๋ ๊ฐ์ ๋ฐ๊ฟ์๋ ์๊ณ , ์ญ์ ํ๋ ๋ฑ ์ ์ ํ ๋์ํ๋ค. (update)
- ๋ํ Observer๋ค์ ์ธ์ ๋ Subject์ ๊ทธ๋ฃน์์ ์ถ๊ฐ/์ญ์ ๋ ์ ์๋ค. Subject ๊ทธ๋ฃน์ ์ถ๊ฐ๋๋ฉด Subject๋ก๋ถํฐ ์ ๋ณด๋ฅผ ์ ๋ฌ๋ฐ๊ฒ ๋ ๊ฒ ์ด๋ฉฐ, ๊ทธ๋ฃน์์ ์ญ์ ๋ ๊ฒฝ์ฐ ๋์ด์ Subject์ ์ ๋ณด๋ฅผ ๋ฐ์์ ์๊ฒ ๋๋ค.
ํด๋์ค ๊ตฌ์กฐ
// ๊ด์ฐฐ ๋์์ / ๋ฐํ์
interface ISubject {
void registerObserver(IObserver o);
void removeObserver(IObserver o);
void notifyObserver();
}
class ConcreteSubject implements ISubject {
// ๊ด์ฐฐ์๋ค์ ๋ฑ๋กํ์ฌ ๋ด๋ ๋ฆฌ์คํธ
List<IObserver> observers = new ArrayList<>();
// ๊ด์ฐฐ์๋ฅผ ๋ฆฌ์คํธ์ ๋ฑ๋ก
@Override
public void registerObserver(IObserver o) {
observers.add(o);
System.out.println(o + " ๊ตฌ๋
์๋ฃ");
}
// ๊ด์ฐฐ์๋ฅผ ๋ฆฌ์คํธ์ ์ ๊ฑฐ
@Override
public void removeObserver(IObserver o) {
observers.remove(o);
System.out.println(o + " ๊ตฌ๋
์ทจ์");
}
// ๊ด์ฐฐ์์๊ฒ ์ด๋ฒคํธ ์ก์
@Override
public void notifyObserver() {
for(IObserver o : observers) { // ๊ด์ฐฐ์ ๋ฆฌ์คํธ๋ฅผ ์ํํ๋ฉฐ
o.update(); // ์์
}
}
}
// ๊ด์ฐฐ์ / ๊ตฌ๋
์
interface IObserver {
void update();
}
class ObserverA implements IObserver {
public void update() {
System.out.println("ObserverA ํํ
์ด๋ฒคํธ ์๋ฆผ์ด ์์ต๋๋ค.");
}
public String toString() { return "ObserverA"; }
}
class ObserverB implements IObserver {
public void update() {
System.out.println("ObserverB ํํ
์ด๋ฒคํธ ์๋ฆผ์ด ์์ต๋๋ค.");
}
public String toString() { return "ObserverB"; }
}
ํด๋์ค ํ๋ฆ
public class Client {
public static void main(String[] args) {
// ๋ฐํ์ ๋ฑ๋ก
ISubject publisher = new ConcreteSubject();
// ๋ฐํ์๋ฅผ ๊ตฌ๋
ํ ๊ด์ฐฐ์๋ค ๋ฆฌ์คํธ๋ก ๋ฑ๋ก
IObserver o1 = new ObserverA();
IObserver o2 = new ObserverB();
publisher.registerObserver(o1);
publisher.registerObserver(o2);
// ๊ด์ฐฐ์์๊ฒ ์ด๋ฒคํธ ์ ํ
publisher.notifyObserver();
// ObserverB๊ฐ ๊ตฌ๋
์ทจ์
publisher.removeObserver(o2);
// ObserverA ํํ
๋ง ์ด๋ฒคํธ ์ ํ
publisher.notifyObserver();
}
}
์ต์ ๋ฒ ํจํด ํน์ง
ํจํด ์ฌ์ฉ ์๊ธฐ
- ์ฑ์ด ํ์ ๋ ์๊ฐ, ํน์ ํ ์ผ์ด์ค์๋ง ๋ค๋ฅธ ๊ฐ์ฒด๋ฅผ ๊ด์ฐฐํด์ผ ํ๋ ๊ฒฝ์ฐ
- ๋์ ๊ฐ์ฒด์ ์ํ๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ๋ค๋ฅธ ๊ฐ์ฒด์ ๋์์ ํธ๋ฆฌ๊ฑฐํด์ผ ํ ๋
- ํ ๊ฐ์ฒด์ ์ํ๊ฐ ๋ณ๊ฒฝ๋๋ฉด ๋ค๋ฅธ ๊ฐ์ฒด๋ ๋ณ๊ฒฝํด์ผ ํ ๋. ๊ทธ๋ฐ๋ฐ ์ด๋ค ๊ฐ์ฒด๋ค์ด ๋ณ๊ฒฝ๋์ด์ผ ํ๋์ง ๋ชฐ๋ผ๋ ๋ ๋
- MVC ํจํด์์๋ ์ฌ์ฉ๋จ (Model, View, Controller)
- MVC์ Model๊ณผ View์ ๊ด๊ณ๋ Observer ํจํด์ Subject ์ญํ ๊ณผ Observer ์ญํ ์ ๊ด๊ณ์ ๋์๋๋ค.
- ํ๋์ Model์ ๋ณต์์ View๊ฐ ๋์ํ๋ค.
ํจํด ์ฅ์
- Subject์ ์ํ ๋ณ๊ฒฝ์ ์ฃผ๊ธฐ์ ์ผ๋ก ์กฐํํ์ง ์๊ณ ์๋์ผ๋ก ๊ฐ์งํ ์ ์๋ค.
- ๋ฐํ์์ ์ฝ๋๋ฅผ ๋ณ๊ฒฝํ์ง ์๊ณ ๋ ์ ๊ตฌ๋ ์ ํด๋์ค๋ฅผ ๋์ ํ ์ ์์ด ๊ฐ๋ฐฉ ํ์ ์์น(OCP) ์ค์ํ๋ค
- ๋ฐํ์ ์์ ์์์ ๋ฐํ์์ ๊ตฌ๋ ์๋ฆผ ๊ด๊ณ๋ฅผ ๋งบ์ ์ ์๋ค.
- ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ ๊ฐ์ฒด(Subject)์ ๋ณ๊ฒฝ์ ๊ฐ์งํ๋ ๊ฐ์ฒด(Observer)์ ๊ด๊ณ๋ฅผ ๋์จํ๊ฒ ์ ์งํ ์ ์๋ค. (๋์จํ ๊ฒฐํฉ)
ํจํด ๋จ์
- ๊ตฌ๋
์๋ ์๋ฆผ ์์๋ฅผ ์ ์ดํ ์ ์๊ณ , ๋ฌด์์ ์์๋ก ์๋ฆผ์ ๋ฐ์
- ํ๋ ์ฝ๋ฉ์ผ๋ก ๊ตฌํํ ์๋ ์๊ฒ ์ง๋ง, ๋ณต์ก์ฑ๊ณผ ๊ฒฐํฉ์ฑ๋ง ๋์์ง๊ธฐ ๋๋ฌธ์ ์ถ์ฒ๋์ง๋ ์๋ ๋ฐฉ๋ฒ์ด๋ค.
- ์ต์ ๋ฒ ํจํด์ ์์ฃผ ๊ตฌ์ฑํ๋ฉด ๊ตฌ์กฐ์ ๋์์ ์์๋ณด๊ธฐ ํ๋ค์ด์ ธ ์ฝ๋ ๋ณต์ก๋๊ฐ ์ฆ๊ฐํ๋ค.
- ๋ค์์ ์ต์ ๋ฒ ๊ฐ์ฒด๋ฅผ ๋ฑ๋ก ์ดํ ํด์งํ์ง ์๋๋ค๋ฉด ๋ฉ๋ชจ๋ฆฌ ๋์๊ฐ ๋ฐ์ํ ์๋ ์๋ค.
์์ ๋ฅผ ํตํด ์์๋ณด๋ Observer ํจํด
๋ ์จ API ๋ก๋ถํฐ ์จ์ต๋ ์๋ฆผ ๋ฐ๊ธฐ
ํ์ฌ ๋ ์จ์ ์จ๋, ์ต๋, ๊ธฐ์ ๋ฐ์ดํฐ๋ฅผ ์ป์์ ์๋ ์๋น์ค์ธ WeatherAPI ํด๋์ค๊ฐ ์๋ค๊ณ ํ์. ๊ฐ ๊ตญ๊ฐ์ ์ฌ์ฉ์๋ค์ด ์ด ๋ ์จ API๋ฅผ ์ด์ฉํด ์จ์ต๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ display ํ๋ค๋ ์๋น์ค๋ฅผ ๊ฐ๋ฐํ๋ ค๊ณ ํ๋ค.
ํด๋ฆฐํ์ง ์์ ๋ฌธ์ ์ ์ฝ๋ โ
๊ทธ๋์ ๋ค์๊ณผ ๊ฐ์ด ํด๋์ค๋ฅผ ๊ตฌ์ฑํ์๋ค. IUser ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ KoreanUser ํด๋์ค์์๋ ์ป์ด์ค๋ ค๋ api์ ๊ฐ์ฒด๋ฅผ ์์ฑ์๋ก ๋ฐ๊ณ ์ฐ๊ฒฐํ์ฌ ์ค์ ์จ์ต๋ ๋ฐ์ดํฐ๋ฅผ ์ถ๋ ฅํ๋๋ก ํ์๋ค.
class WeatherAPI {
float temp; // ์จ๋
float humidity; // ์ต๋
float pressure; // ๊ธฐ์
void measurementsChanged() {
// ํ์ฌ์ ์จ์ต๋ ๋ฐ์ดํฐ๋ฅผ ๋๋ค๊ฐ์ผ๋ก ์ป๋ ๊ฒ์ผ๋ก ๋น์ ํ์๋ค.
temp = new Random().nextFloat() * 100;
humidity = new Random().nextFloat() * 100;
pressure = new Random().nextFloat() * 100;
}
}
interface IUser {
void display();
}
class KoreanUser implements IUser {
WeatherAPI api; // api์๋น์ค ๊ฐ์ฒด๋ฅผ ํฉ์ฑํจ
String name;
KoreanUser(String name, WeatherAPI api) {
this.name = name;
this.api = api;
}
public void display() {
System.out.printf("%s๋์ด ํ์ฌ ๋ ์จ ์ํ๋ฅผ ์กฐํํจ : %.2f°C %.2fg/m3 %.2fhPa\n", name, api.temp, api.humidity, api.pressure);
}
}
public class Client {
public static void main(String[] args) {
WeatherAPI api = new WeatherAPI();
List<IUser> users = new ArrayList<>();
users.add(new KoreanUser("ํ๊ธธ๋", api));
users.add(new KoreanUser("์๊บฝ์ ", api));
users.add(new KoreanUser("์ธ์ข
๋์", api));
// ์จ์ต๋๊ธฐ์์ ํ์ฌ ์ํ์ ์จ์ต๋ ์ ๋ณด๊ฐ ๊ฐฑ์ ๋จ
api.measurementsChanged();
for(IUser u: users) {
u.display();
}
api.measurementsChanged();
// ...
}
}
๋น์ฅ ๋์์ ์์ด์ ๋ฌธ์ ๊ฐ ์์ด๋ณด์ด์ง๋ง, ํ๋์ ์ฒ ๋ด๋ ๋๋ฌด Client ํด๋์ค์ ์ฝ๋๊ฐ ์ง์ฝ์ ์ผ๋ก ๊ตฌ์ฑ๋จ์ ๋ณผ ์ ์๋ค. ์๋ํ๋ฉด ๋ ์จ API๋ฅผ ์ ์ฒญํ ์ ์ ๋ค์ ๊ด๋ฆฌํ๋ ํ๋ก์ธ์ค๋ฅผ ํด๋ผ์ด์ธํธ์์ ๊ด๋ฆฌํ๊ณ ์์ผ๋ฉฐ, ์ ์ ๋ค์ ๋ ์จ ๋ฐ์ดํฐ๊ฐ ๊ฐฑ์ ๋ ๋(measurementsChanged) ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌ๋ฐ์ display ํ๋ ๊ฒ์ด ์๋ ์ ์ ํด๋์ค๋ด์ ์๋ api ๊ฐ์ฒด์ ์์ ์ด ์ง์ ์กฐํํ์ฌ display ํ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ด๋ค.
์ต์ ๋ฒ ํจํด์ ์ ์ฉํ ์ฝ๋ โ๏ธ
๋ฐ๋ผ์ ๋ ์จ API๋ฅผ ์ ์ฒญํ ์ ์ ๋ค์ ๊ด๋ฆฌํ๋ ํ๋ก์ธ์ค๋ฅผ ํด๋ผ์ด์ธํธ๊ฐ ์๋ ๋ ์จ API ํด๋์ค์์ ๊ด๋ฆฌํ๋๋ก ์ค์ ํ๊ณ , ๊ฐ ์ ์ ๋ค์ด ๋ณํ๋ ๋ ์จ ๋ฐ์ดํฐ๋ฅผ ์๋์ผ๋ก ์ ๋ฌ๋ฐ์ ์ ์๋๋ก ์ผ์ข ์ ์ ํ ๋ฐ ๋์ ํ์๋ฅผ ๋ ์จ API ํด๋์ค์์ ๊ตฌํํ ํ์์ฑ์ด ์๋ค.
์ฆ, WeatherAPI ํด๋์ค๋ฅผ ๋ฐํ์, ๊ด์ฐฐ ๋์์๋ก์ Subject ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋๋ก ํ๊ณ , API ์ฌ์ฉ์๋ค์ ๊ด์ฐฐ์, ๊ตฌ๋ ์ ๋ก์ Observer ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋๋ก ๊ตฌ์ฑํ๋ค.
interface Subject {
void registerObserver(Observer o); // ๊ตฌ๋
์ถ๊ฐ
void removeObserver(Observer o); // ๊ตฌ๋
์ญ์
void notifyObservers(); // Subject ๊ฐ์ฒด์ ์ํ ๋ณ๊ฒฝ์ ์ด๋ฅผ ๋ชจ๋ ์ต์ ๋ฒ์๊ฒ ์flsek
}
class WeatherAPI implements Subject{
float temp; // ์จ๋
float humidity; // ์ต๋
float pressure; // ๊ธฐ์
// ๊ตฌ๋
์๋ค์ ๋ด์ ๊ด๋ฆฌํ๋ ๋ฆฌ์คํธ
List<Observer> subscribers = new ArrayList<>();
void measurementsChanged() {
// ํ์ฌ์ ์จ์ต๋ ๋ฐ์ดํฐ๋ฅผ ๋๋ค๊ฐ์ผ๋ก ์ป๋ ๊ฒ์ผ๋ก ๋น์ ํ์๋ค.
temp = new Random().nextFloat() * 100;
humidity = new Random().nextFloat() * 100;
pressure = new Random().nextFloat() * 100;
notifyObservers(); // ์จ์ต๋ ๊ฐ์ด ๋ณํํ๋ฉด ๋ฐ๋ก ์ต์ ๋ฒ๋ค์๊ฒ ๋ฐํ
}
@Override
public void registerObserver(Observer o) {
subscribers.add(o);
}
@Override
public void removeObserver(Observer o) {
subscribers.remove(o);
}
// ์ด๋ฒคํธ ์ ํ
@Override
public void notifyObservers() {
for(Observer o: subscribers) {
o.display(this); // ์์ ์ ๊ฐ์ฒด๋ฅผ ๋งค๊ฐ๋ณ์๋ก ์ค์ ํ์ฌ ์์ ์ ์ํ๋ฅผ ๊ตฌ๋
์์๊ฒ ์๋ฆผ
}
}
}
interface Observer {
void display(WeatherAPI api);
}
class KoreanUser implements Observer {
String name;
KoreanUser(String name) {
this.name = name;
}
public void display(WeatherAPI api) {
System.out.printf("%s๋์ด ํ์ฌ ๋ ์จ ์ํ๋ฅผ ์กฐํํจ : %.2f°C %.2fg/m3 %.2fhPa\n", name, api.temp, api.humidity, api.pressure);
}
}
public class Client {
public static void main(String[] args) {
WeatherAPI api = new WeatherAPI();
api.registerObserver(new KoreanUser("ํ๊ธธ๋"));
api.registerObserver(new KoreanUser("์๊บฝ์ "));
api.registerObserver(new KoreanUser("์ธ์ข
๋์"));
// ์จ์ต๋๊ธฐ์์ ํ์ฌ ์ํ์ ์จ์ต๋ ์ ๋ณด๊ฐ ๊ฐฑ์ ๋จ
api.measurementsChanged();
// ์์์ ์ ํ๋์ด ์ถ๋ ฅ
api.measurementsChanged();
// ...
}
}
ํด๋ผ์ด์ธํธ์์ ๋ณ๋ค๋ฅธ ๋ช ๋ น์์ด ์์์ ์๋์ผ๋ก ๋ฐํ์๋ก๋ถํฐ ๊ตฌ๋ ์๋ค์ด ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌ๋ฐ์ ์ ์ ํ ์๋์ผ๋ก ์ฒ๋ฆฌํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค. ํต์ฌ์ ๊ตฌ๋ ์๋ค์ ๊ด๋ฆฌ๋ฅผ ๋ฐํ์ ๊ฐ์ฒด์์ ๋ฆฌ์คํธ๋ก ํ๋ค๋ ์ ๊ณผ ๋ฆฌ์คํธ์ ์๋ ๋ชจ๋ ๊ตฌ๋ ์๋ค์ ๋ฉ์๋๋ฅผ ์์ํ์ฌ ์คํํจ์ผ๋ก์จ ๋ณํ๊ฐ ์์๋๋ง๋ค ์ด๋ฒคํธ์ฑ ์๋ฆผ์ผ๋ก ์ ํํ๋ค๋ ์ปจ์ ์ผ๋ก์จ ์ต์ ๋ฒ ํจํด์ ์ดํดํ๋ฉด ๋๋ค.
์ค๋ฌด์์ ์ฐพ์๋ณด๋ Observer ํจํด
์๋ฐ์ ๋ด์ฅ ์ต์ ๋ฒ ๊ฐ์ฒด
ํ๋ก์ ๋์์ธ ํจํด์ ๋ํด์ ์๋ฐ๊ฐ ๋ณ๋๋ก java.lang.reflect.Proxy ๋ก ๋ด์ฅ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์ง์ํ๋ฏ์ด, ์ต์ ๋ฒ ํจํด์ ์ง์ ๊ตฌํํ ์๋ ์์ง๋ง ์๋ฐ์์ java.util.Observable(์ธํฐํ์ด์ค), java.util.Observer(ํด๋์ค) ๋ก ๋ด์ฅ ์ต์ ๋ฒ ๊ฐ์ฒด๋ฅผ ์ง์ํ๋ค. ์ด ๋ด์ฅ ๊ฐ์ฒด๋ฅผ ์ด์ฉํ๋ฉด ์ต์ ๋ฒ ํจํด์ ์ง์ ๊ตฌํํ ํ์ ์์ด ๊ฐ๋จํ ํด๋น ํด๋์ค๋ฅผ ์์ํ๊ธฐ๋ง ํ๋ฉด ์ต์ ๋ฒ ๊ตฌ์กฐ๋ฅผ ์ด์ฉํ ์๊ฐ ์๋ค.
java.util.Observer
Observer ์ธํฐํ์ด์ค๋ฅผ implements ํ ํด๋์ค๋ ์ต์ ๋ฒ ํด๋์ค๊ฐ ๋๊ฒ ๋๋ค. ์ต์ ๋ฒ ํด๋์ค๋ค์ update() ์ถ์ ๋ฉ์๋๋ฅผ ๊ตฌํํจ์ผ๋ก์จ, Observable ํด๋์ค๋ก ๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ์์ ๋ฐ๊ฒ ๋๋ค.
- update(Observable o , Object arg) : Observable์๊ฒ ์ ๋ฌ๋ฐ์ ์๋ก์ด ๋ฐ์ดํฐ๋ฅผ ๊ฐฑ์ ํ๋ค.
java.util.Observable
Observable ํด๋์ค๋ ๋ฑ๋ก๋ ์ต์ ๋ฒ๋ค์ ๊ด๋ฆฌํ๋ฉฐ, ์๋ก์ด ๋ฐ์ดํฐ๊ฐ ๋ค์ด์ค๋ฉด ์ต์ ๋ฒ์๊ฒ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๋ ๋ฐํ์ ์ญํ ์ ํ๋ค. ์๋ ํด๋์ค ๊ตฌํ๋ถ๋ฅผ ๋ณด๋ฉด Vector ์ปฌ๋ ์ ์ ํตํด ์ต์ ๋ฒ๋ค์ ๊ด๋ฆฌํ๋๊ฑธ ๋ณผ ์ ์๋ค. ๋ํ Observable์ ๊ฐ์ฅ ํฐ ํน์ง์ผ๋ก๋ ๋๊ธฐํ(Synchronized)๊ฐ ๊ฑธ๋ ค์์ด Thread-Safe ํ๋ค๋ ๊ฒ์ด๋ค. ์ด Observable ํด๋์ค๋ฅผ ์์(extends)ํ ํด๋์ค๋ ๋ฐํ์ ์ญํ ๋ก์ ์ํํ๊ฒ ๋๋ค.
- addObserver(Observer o) : ์ต์ ๋ฒ๋ฅผ ๋ฆฌ์คํธ์ ์ถ๊ฐ
- notifyObservers(): ์๋ก์ด ๋ฐ์ดํฐ๊ฐ ๋ค์ด์ค๋ฉด ๋ฑ๋ก๋ ์ต์ ๋ฒ์ ์๋ก์ด ๋ฐ์ดํฐ์, ํ๋ผ๋ฏธํฐ๋ฅผ ์ ๋ฌ
- deleteObservers() : ๋ฑ๋ก๋ ๋ชจ๋ ์ต์ ๋ฒ๋ฅผ ์ ๊ฑฐ
- setChanged() : ์ ๊ท ๋ฐ์ดํฐ๊ฐ ๋ค์ด์ค๋ฉด changed ๊ฐ์ true๋ก ๋ณ๊ฒฝ. (changed ๋ณ์๊ฐ true ์ผ๋๋ง ๋ฐ์ดํฐ๊ฐ ์ต์ ๋ฒ์ ์ ๋ฌ๋๋ค)
- clearChanged() : changed ๊ฐ์ false ๋ก ๋ณ๊ฒฝํ๋ค. ์ต์ ๋ฒ์๊ฒ ๋ฐํ์ ๋ฉ์ถ๋ค
- hasChanged() : ํ์ฌ changed ์ ๊ฐ์ ๋ฐํ
- countObservers() : ํ์ฌ ๋ฑ๋ก๋์ด์๋ ์ต์ ๋ฒ์ ์๋ฅผ ๋ฐํ
๋ด์ฅ ์ต์ ๋ฒ ๊ฐ์ฒด๋ก ์ต์ ๋ฒ ํจํด ๊ตฌํํ๊ธฐ
java.util.Observable, java.util.Observer ๋ฅผ ์ด์ฉํด ์์ WeatherAPI ์์ ๋ฅผ ์ฌ๊ตฌ์ฑํด๋ณด์.
๋ฐํ์ ์ญํ ์ด ๋๋ WeatherAPI ํด๋์ค์ Observable์ ์์ํด์ฃผ๊ณ ๋ถ๋ชจ ์ต์ ๋ธ ๋ฉ์๋๋ฅผ ํธ์ถํ๋๋ก ๋ณ๊ฒฝํ๋ค.
class WeatherAPI extends Observable {
float temp; // ์จ๋
float humidity; // ์ต๋
float pressure; // ๊ธฐ์
void measurementsChanged() {
// ํ์ฌ์ ์จ์ต๋ ๋ฐ์ดํฐ๋ฅผ ๋๋ค๊ฐ์ผ๋ก ์ป๋ ๊ฒ์ผ๋ก ๋น์ ํ์๋ค.
temp = new Random().nextFloat() * 100;
humidity = new Random().nextFloat() * 100;
pressure = new Random().nextFloat() * 100;
/* ๋ถ๋ชจ ํด๋์ค Observable์ ๋ถ๋ชจ ๋ฉ์๋ */
setChanged(); // ๋ด๋ถ ํ๋๊ทธ๋ฅผ true ๋ก ๋ง๋ค์ด ์๋ฆผ์ด ๋์ํ๊ฒ ๋ ํ๋ค
notifyObservers(); // ์ต์ ๋ฒ๋ค์๊ฒ ์๋ฆผ ์ ํ
}
}
๊ทธ๋ฆฌ๊ณ ์ต์ ๋ฒ ์ญํ ์ด ๋๋ KoreanUser์ Observer ์ธํฐํ์ด์ค๋ฅผ implements ํด์ฃผ๊ณ update() ๋ฉ์๋๋ฅผ ๊ตฌํํจ์ผ๋ก์จ Observable์ ์ด๋ฒคํธ๋ฅผ ์์ ํ๊ฒ ๋๋ค.
class KoreanUser implements Observer {
String name;
KoreanUser(String name) {
this.name = name;
}
public void display(WeatherAPI api) {
System.out.printf("%s๋์ด ํ์ฌ ๋ ์จ ์ํ๋ฅผ ์กฐํํจ : %.2f°C %.2fg/m3 %.2fhPa\n", name, api.temp, api.humidity, api.pressure);
}
@Override
public void update(Observable o, Object arg) {
// ๋ฐํ์๊ฐ WeatherAPI ์ธ ๊ฒฝ์ฐ (Observable์ ์์ํ ๋ชจ๋ ํด๋์ค์์ ๋ฐํ์ด ๊ฐ๋ฅํ๋ ๊ตฌ๋ถํด ์ฃผ์ด์ผ ํ๋ค)
if(o instanceof WeatherAPI) {
WeatherAPI w = (WeatherAPI) o; // ๋ค์ด ์บ์คํ
display(w);
}
}
}
์ด์ ๋ฉ์ธ ๋ฉ์๋์์ Observable์ ๋ฉ์๋์ธ addObserver() ์ ํธ์ถํ๋๋ก ๋ณ๊ฒฝํด์ฃผ๋ฉด ๋ด์ฅ ์ต์ ๋ฒ ํจํด์ด ์์ฑ๋๋ค.
public static void main(String[] args) {
WeatherAPI api = new WeatherAPI();
// ์ต์ ๋ฒ ๋ฆฌ์คํธ ์ถ๊ฐ
api.addObserver(new KoreanUser("ํ๊ธธ๋"));
api.addObserver(new KoreanUser("์๊บฝ์ "));
api.addObserver(new KoreanUser("์ธ์ข
๋์"));
// ์จ์ต๋๊ธฐ์์ ํ์ฌ ์ํ์ ์จ์ต๋ ์ ๋ณด๊ฐ ๊ฐฑ์ ๋จ
api.measurementsChanged();
// ์์์ ์ ํ๋์ด ์ถ๋ ฅ
api.measurementsChanged();
// ...
}
๋ด์ฅ ์ต์ ๋ฒ ๊ฐ์ฒด์ ํ๊ณ
์ต์ ๋ฒ ํจํด์ ์ค๋ฌด์์ ๋ง์ด ์ฌ์ฉํ๊ณ ์ ์ฉํ ํจํด์ด๋ ์๋ฐ์์ ๋ฐ๋ก ๋ด์ฅ ๊ฐ์ฒด๋ก ์ง์๋๋ค. ๊ทธ๋ผ ์ต์ ๋ฒ ํจํด์ ๊ตฌํํ ์ผ์ด ์๋ค๋ฉด ๋ด์ฅ ์ต์ ๋ฒ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ฉด ๋๊ฒ ๋ค๊ณ ์๊ฐํ ์๋ ์์ง๋ง ์น๋ช ์ ์ธ ํ๊ณ์ ์ด ์กด์ฌํ๋ค. ์๋ํ๋ฉด java.util.Observable์ ํด๋์ค์ด๊ธฐ ๋๋ฌธ์ ์์์ ํด์ผ ๋๋ค๋ ์ ์ธ๋ฐ, ์๋ฐ์์๋ ๋จ์ผ ์์๋ง ์ง์ํ๊ธฐ ๋๋ฌธ์, ๋ง์ผ ๋ฐํ์ ์ญํ ์ ํด์ผ ํ๋ ํด๋์ค๊ฐ ๋ค๋ฅธ ํด๋์ค๋ฅผ ์์ํ๊ณ ์๋ ์ํ๋ผ๋ฉด java.util.Observable ํด๋์ค์ ํ์ ํด๋์ค๋ก ํ ์ ์๊ฒ ๋๊ธฐ ๋๋ฌธ์ด๋ค.
๊ทธ๋ผ ํฉ์ฑ(composition)์ ํตํด ๋ฉ์๋ ์์์ผ๋ก ๊ตฌ์ฑํด์ฃผ๋ฉด ๋์ง ์์๊ฐ ์ถ์ง๋ง, Observable ํด๋์ค๋ฅผ ์ดํด๋ณด๋ฉด setChanged() ๋ฉ์๋๊ฐ protected๋ก ์ ์ธ๋์ด ์๋ค. ์ฆ, Observable์ ์์ ํด๋์ค์์๋ง ํธ์ถํ ์ ์๋ค๋ ๊ฒ์ด๋ค.
๋ฐ๋ผ์ ๊ทผ๋ณธ์ ์ธ ํด๊ฒฐ์ฑ ์ ๊ฒฐ๊ตญ ๋์์ธ ํจํด์ผ๋ก์ ์ต์ ๋ฒ ํจํด์ ๊ฐ๋ฐ์๊ฐ ์ง์ ๊ตฌํํด์ผ ํ๋ค๋ ์ ์ด๋ค. ์ง๊ธ๊น์ง ์ฐ๋ฆฌ๊ฐ ์ต์ ๋ฒ ํจํด์ ์ฐ์ตํ ๋ณด๋์ด ์๋ ๊ฒ์ด๋ค.
์ด์ธ์ Java ์ฌ์ฉ์ฒ
- java.util.Observer / java.util.Observable
- java.util.EventListener์ ๋ชจ๋ ๊ตฌํ์ฒด
- javax.servlet.http.HttpSessionBindingListener
- javax.servlet.http.HttpSessionAttributeListener
- javax.faces.event.PhaseListener
# ์ฐธ๊ณ ์๋ฃ
์ฝ๋ฉ์ผ๋ก ํ์ตํ๋ GoF์ ๋์์ธ ํจํด - ๋ฐฑ๊ธฐ์
https://refactoring.guru/design-patterns/observer
https://xzio.tistory.com/289