๋””์ž์ธ ํŒจํ„ด/GOF

๐Ÿ’  ์˜ต์ €๋ฒ„(Observer) ํŒจํ„ด - ์™„๋ฒฝ ๋งˆ์Šคํ„ฐํ•˜๊ธฐ

์ธํŒŒ_ 2023. 1. 27. 08:03

gof-Observer-pattern
gof-Observer-pattern

Observer Pattern

์˜ต์ €๋ฒ„ ํŒจํ„ด(Observer Pattern)์€ ์˜ต์ €๋ฒ„(๊ด€์ฐฐ์ž)๋“ค์ด ๊ด€์ฐฐํ•˜๊ณ  ์žˆ๋Š” ๋Œ€์ƒ์ž์˜ ์ƒํƒœ๊ฐ€ ๋ณ€ํ™”๊ฐ€ ์žˆ์„ ๋•Œ๋งˆ๋‹ค ๋Œ€์ƒ์ž๋Š” ์ง์ ‘ ๋ชฉ๋ก์˜ ๊ฐ ๊ด€์ฐฐ์ž๋“ค์—๊ฒŒ ํ†ต์ง€ํ•˜๊ณ , ๊ด€์ฐฐ์ž๋“ค์€ ์•Œ๋ฆผ์„ ๋ฐ›์•„ ์กฐ์น˜๋ฅผ ์ทจํ•˜๋Š” ํ–‰๋™ ํŒจํ„ด์ด๋‹ค.

์˜ต์ €๋ฒ„ ํŒจํ„ด์€ ์—ฌํƒ€ ๋‹ค๋ฅธ ๋””์ž์ธ ํŒจํ„ด๋“ค๊ณผ ๋‹ค๋ฅด๊ฒŒ ์ผ๋Œ€๋‹ค(one-to-many) ์˜์กด์„ฑ์„ ๊ฐ€์ง€๋Š”๋ฐ, ์ฃผ๋กœ ๋ถ„์‚ฐ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋ง ์‹œ์Šคํ…œ์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋œ๋‹ค. Pub/Sub(๋ฐœํ–‰/๊ตฌ๋…) ๋ชจ๋ธ๋กœ๋„ ์•Œ๋ ค์ ธ ์žˆ๊ธฐ๋„ ํ•˜๋‹ค.

์ด ํŒจํ„ด์„ ์ดํ•ดํ•˜๋Š”๋ฐ ์žˆ์–ด ๋‰ด์Šค ํ”ผ๋“œ๋‚˜ ์œ ํŠœ๋ธŒ๋กœ ๋น„์œ ํ•ด๋ณด๋ฉด ์‰ฝ๋‹ค. ์œ ํŠœ๋ธŒ ์ฑ„๋„์€ ๋ฐœํ–‰์ž(Subject)๊ฐ€ ๋˜๊ณ  ๊ตฌ๋…์ž๋“ค์€ ๊ด€์ฐฐ์ž(Observer)๊ฐ€ ๋˜๋Š” ๊ตฌ์กฐ๋กœ ๋ณด๋ฉด ๋œ๋‹ค. ์‹ค์ œ๋กœ ์œ ํŠœ๋ฒ„๊ฐ€ ์˜์ƒ์„ ์˜ฌ๋ฆฌ๋ฉด ์—ฌ๋Ÿฌ๋ช…์˜ ๊ตฌ๋…์ž๋“ค์€ ๋ชจ๋‘ ์˜์ƒ์ด ์˜ฌ๋ผ์™”๋‹ค๋Š” ์•Œ๋ฆผ์„ ๋ฐ›๋Š”๋ฐ, ์ด๋ฅผ ํŒจํ„ด ๊ตฌ์กฐ๋กœ ๋ณด์ž๋ฉด ๊ตฌ๋…์ž๋“ค์€ ํ•ด๋‹น ์ฑ„๋„์„ ๊ตฌ๋…ํ•จ์œผ๋กœ์จ ์ฑ„๋„์— ์–ด๋– ํ•œ ๋ณ€ํ™”(์˜์ƒ์„ ์˜ฌ๋ฆฌ๊ฑฐ๋‚˜ ์ปค๋ฎค๋‹ˆํ‹ฐ์— ๊ธ€์„ ์“ฐ๊ฑฐ๋‚˜)๊ฐ€ ์ƒ๊ธฐ๊ฒŒ ๋˜๋ฉด ๋ฐ”๋กœ ์—ฐ๋ฝ์„ ๋ฐ›์•„ ํƒ์ง€ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ๋ฐ˜๋ฉด ๊ตฌ๋…์„ ํ•ด์ง€ํ•˜๊ฑฐ๋‚˜ ์•ˆํ•œ ์‹œ์ฒญ์ž์—๊ฒŒ๋Š” ์•Œ๋ฆผ์ด ๊ฐ€์ง€์•Š๊ฒŒ ๋œ๋‹ค.

gof-Observer-pattern
https://stackabuse.com/observer-design-pattern-in-python/

ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ ์œผ๋กœ ์˜ต์ €๋ฒ„ ํŒจํ„ด์€ ์‚ฌ์‹ค '๊ด€์ฐฐ' ํ•˜๊ธฐ ๋ณด๋‹จ ๊ฐฑ์‹ ์„ ์œ„ํ•œ ํžŒํŠธ ์ •๋ณด๋ฅผ '์ „๋‹ฌ' ๋ฐ›๊ธธ ๊ธฐ๋‹ค๋ฆฐ๋‹ค๊ณ  ๋ณด๋Š” ๊ฒƒ์ด ์ ์ ˆํ•˜๋‹ค. ๊ด€์ฐฐ์ž๋ผ๋Š” ๋‹จ์–ด ๋‰˜์•™์Šค์—์„œ ๋Šฅ๋™์ ์œผ๋กœ ๋Œ€์ƒ์„ ๊ด€์ฐฐํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋Š๊ปด์ง€์ง€๋งŒ, ์‚ฌ์‹ค ๋Œ€์ƒ ๊ฐ์ฒด๋กœ๋ถ€ํ„ฐ ์ˆ˜๋™์ ์œผ๋กœ ์ „๋‹ฌ ๋ฐ›๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์˜ต์ €๋ฒ„ ํŒจํ„ด ๊ตฌ์กฐ

gof-Observer-pattern

  • ISubject : ๊ด€์ฐฐ ๋Œ€์ƒ์ž๋ฅผ ์ •์˜ํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค
  • ConcreteSubject : ๊ด€์ฐฐ ๋‹นํ•˜๋Š” ๋Œ€์ƒ์ž / ๋ฐœํ–‰์ž / ๊ฒŒ์‹œ์ž
    • Observer๋“ค์„ ๋ฆฌ์ŠคํŠธ(List, Map, Set ..๋“ฑ)๋กœ ๋ชจ์•„ ํ•ฉ์„ฑ(compositoin)ํ•˜์—ฌ ๊ฐ€์ง€๊ณ  ์žˆ์Œ
    • Subject์˜ ์—ญํ• ์€ ๊ด€์ฐฐ์ž์ธ Observer๋“ค์„ ๋‚ด๋ถ€ ๋ฆฌ์ŠคํŠธ์— ๋“ฑ๋ก/์‚ญ์ œ ํ•˜๋Š” ์ธํ”„๋ผ๋ฅผ ๊ฐ–๊ณ  ์žˆ๋‹ค. (register, remove)
    • Subject๊ฐ€ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜ ์–ด๋–ค ๋™์ž‘์„ ์‹คํ–‰ํ• ๋•Œ, Observer ๋“ค์—๊ฒŒ ์ด๋ฒคํŠธ ์•Œ๋ฆผ(notify)์„ ๋ฐœํ–‰ํ•œ๋‹ค.
  • IObserver : ๊ตฌ๋…์ž๋“ค์„ ๋ฌถ๋Š” ์ธํ„ฐํŽ˜์ด์Šค (๋‹คํ˜•์„ฑ)
  • Observer : ๊ด€์ฐฐ์ž / ๊ตฌ๋…์ž / ์•Œ๋ฆผ ์ˆ˜์‹ ์ž.
    • Observer๋“ค์€ Subject๊ฐ€ ๋ฐœํ–‰ํ•œ ์•Œ๋ฆผ์— ๋Œ€ํ•ด ํ˜„์žฌ ์ƒํƒœ๋ฅผ ์ทจ๋“ํ•œ๋‹ค.
    • Subject์˜ ์—…๋ฐ์ดํŠธ์— ๋Œ€ํ•ด ์ „ํ›„ ์ •๋ณด๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค.

์˜ต์ €๋ฒ„ ํŒจํ„ด์€ ์—ฌํƒ€ ๋‹ค๋ฅธ ๋””์ž์ธ ํŒจํ„ด๊ณผ ๋˜‘๊ฐ™์ด ์ƒํ˜ธ์ž‘์šฉํ•  ๊ฐ์ฒด๋ฅผ ํ•ฉ์„ฑ(compositoin)์„ ํ•˜๊ณ  ๋ฉ”์„œ๋“œ ์œ„์ž„์„ ํ†ตํ•ด ๊ตฌ์„ฑํ•˜๋Š” ์ฝ”๋“œ ํŒจํ„ด์ž„์€ ๋˜‘๊ฐ™์ง€๋งŒ, ํ•ต์‹ฌ์€ ํ•ฉ์„ฑํ•œ ๊ฐ์ฒด๋ฅผ ๋ฆฌ์ŠคํŠธ๋กœ ๊ด€๋ฆฌํ•˜๊ณ  ๋ฆฌ์ŠคํŠธ์— ์žˆ๋Š” ๊ด€์ฐฐ์ž ๊ฐ์ฒด๋“ค์—๊ฒŒ ๋ชจ๋‘ ๋ฉ”์„œ๋“œ ์œ„์ž„์„ ํ†ตํ•œ ์ „ํŒŒ ํ–‰์œ„๋ฅผ ํ•œ๋‹ค๋Š” ์ ์„ ๊ธฐ์–ตํ•˜๋ฉด ๋œ๋‹ค. 


์˜ต์ €๋ฒ„ ํŒจํ„ด ํ๋ฆ„

  1. ์˜ต์ €๋ฒ„ ํŒจํ„ด์—์„œ๋Š” ํ•œ๊ฐœ์˜ ๊ด€์ฐฐ ๋Œ€์ƒ์ž(Subject)์™€ ์—ฌ๋Ÿฌ๊ฐœ์˜ ๊ด€์ฐฐ์ž(Observer A, B, C)๋กœ ์ผ ๋Œ€ ๋‹ค ๊ด€๊ณ„๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋‹ค.
  2. Observer ํŒจํ„ด์—์„œ๋Š” ๊ด€์ฐฐ ๋Œ€์ƒ Subject์˜ ์ƒํƒœ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์˜ต์ €๋ฒ„ ํ•œํƒœ ํ†ต๋ณดํ•ด์ค€๋‹ค. (notifyObserver)
  3. ๋Œ€์ƒ์ž๋กœ๋ถ€ํ„ฐ ํ†ต๋ณด๋ฅผ ๋ฐ›์€ Observer๋Š” ๊ฐ’์„ ๋ฐ”๊ฟ€์ˆ˜๋„ ์žˆ๊ณ , ์‚ญ์ œํ•˜๋Š” ๋“ฑ ์ ์ ˆํžˆ ๋Œ€์‘ํ•œ๋‹ค. (update)
  4. ๋˜ํ•œ Observer๋“ค์€ ์–ธ์ œ๋“  Subject์˜ ๊ทธ๋ฃน์—์„œ ์ถ”๊ฐ€/์‚ญ์ œ ๋  ์ˆ˜ ์žˆ๋‹ค. Subject ๊ทธ๋ฃน์— ์ถ”๊ฐ€๋˜๋ฉด Subject๋กœ๋ถ€ํ„ฐ ์ •๋ณด๋ฅผ ์ „๋‹ฌ๋ฐ›๊ฒŒ ๋  ๊ฒƒ ์ด๋ฉฐ, ๊ทธ๋ฃน์—์„œ ์‚ญ์ œ๋  ๊ฒฝ์šฐ ๋”์ด์ƒ Subject์˜ ์ •๋ณด๋ฅผ ๋ฐ›์„์ˆ˜ ์—†๊ฒŒ ๋œ๋‹ค.

gof-Observer-pattern
https://www.scaler.com/topics/design-patterns/observer-design-pattern/

 

ํด๋ž˜์Šค ๊ตฌ์กฐ

// ๊ด€์ฐฐ ๋Œ€์ƒ์ž / ๋ฐœํ–‰์ž
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();
    }
}

Observer-pattern


์˜ต์ €๋ฒ„ ํŒจํ„ด ํŠน์ง•

 

ํŒจํ„ด ์‚ฌ์šฉ ์‹œ๊ธฐ

  • ์•ฑ์ด ํ•œ์ •๋œ ์‹œ๊ฐ„, ํŠน์ •ํ•œ ์ผ€์ด์Šค์—๋งŒ ๋‹ค๋ฅธ ๊ฐ์ฒด๋ฅผ ๊ด€์ฐฐํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ
  • ๋Œ€์ƒ ๊ฐ์ฒด์˜ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ๋‹ค๋ฅธ ๊ฐ์ฒด์˜ ๋™์ž‘์„ ํŠธ๋ฆฌ๊ฑฐํ•ด์•ผ ํ• ๋•Œ
  • ํ•œ ๊ฐ์ฒด์˜ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ๋‹ค๋ฅธ ๊ฐ์ฒด๋„ ๋ณ€๊ฒฝํ•ด์•ผ ํ• ๋•Œ. ๊ทธ๋Ÿฐ๋ฐ ์–ด๋–ค ๊ฐ์ฒด๋“ค์ด ๋ณ€๊ฒฝ๋˜์–ด์•ผ ํ•˜๋Š”์ง€ ๋ชฐ๋ผ๋„ ๋  ๋•Œ
  • MVC ํŒจํ„ด์—์„œ๋„ ์‚ฌ์šฉ๋จ (Model, View, Controller)
    • MVC์˜ Model๊ณผ View์˜ ๊ด€๊ณ„๋Š” Observer ํŒจํ„ด์˜ Subject ์—ญํ• ๊ณผ Observer ์—ญํ• ์˜ ๊ด€๊ณ„์— ๋Œ€์‘๋œ๋‹ค.
    • ํ•˜๋‚˜์˜ Model์— ๋ณต์ˆ˜์˜ View๊ฐ€ ๋Œ€์‘ํ•œ๋‹ค.

 

ํŒจํ„ด ์žฅ์ 

  • Subject์˜ ์ƒํƒœ ๋ณ€๊ฒฝ์„ ์ฃผ๊ธฐ์ ์œผ๋กœ ์กฐํšŒํ•˜์ง€ ์•Š๊ณ  ์ž๋™์œผ๋กœ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋ฐœํ–‰์ž์˜ ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ ๋„ ์ƒˆ ๊ตฌ๋…์ž ํด๋ž˜์Šค๋ฅผ ๋„์ž…ํ•  ์ˆ˜ ์žˆ์–ด ๊ฐœ๋ฐฉ ํ์‡„ ์›์น™(OCP) ์ค€์ˆ˜ํ•œ๋‹ค
  • ๋Ÿฐํƒ€์ž„ ์‹œ์ ์—์„œ์— ๋ฐœํ–‰์ž์™€ ๊ตฌ๋… ์•Œ๋ฆผ ๊ด€๊ณ„๋ฅผ ๋งบ์„ ์ˆ˜ ์žˆ๋‹ค.
  • ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฐ์ฒด(Subject)์™€ ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ•˜๋Š” ๊ฐ์ฒด(Observer)์˜ ๊ด€๊ณ„๋ฅผ ๋Š์Šจํ•˜๊ฒŒ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค. (๋Š์Šจํ•œ ๊ฒฐํ•ฉ)

 

ํŒจํ„ด ๋‹จ์ 

  • ๊ตฌ๋…์ž๋Š” ์•Œ๋ฆผ ์ˆœ์„œ๋ฅผ ์ œ์–ดํ• ์ˆ˜ ์—†๊ณ , ๋ฌด์ž‘์œ„ ์ˆœ์„œ๋กœ ์•Œ๋ฆผ์„ ๋ฐ›์Œ
    • ํ•˜๋“œ ์ฝ”๋”ฉ์œผ๋กœ ๊ตฌํ˜„ํ• ์ˆ˜๋Š” ์žˆ๊ฒ ์ง€๋งŒ, ๋ณต์žก์„ฑ๊ณผ ๊ฒฐํ•ฉ์„ฑ๋งŒ ๋†’์•„์ง€๊ธฐ ๋•Œ๋ฌธ์— ์ถ”์ฒœ๋˜์ง€๋Š” ์•Š๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.
  • ์˜ต์ €๋ฒ„ ํŒจํ„ด์„ ์ž์ฃผ ๊ตฌ์„ฑํ•˜๋ฉด ๊ตฌ์กฐ์™€ ๋™์ž‘์„ ์•Œ์•„๋ณด๊ธฐ ํž˜๋“ค์–ด์ ธ ์ฝ”๋“œ ๋ณต์žก๋„๊ฐ€ ์ฆ๊ฐ€ํ•œ๋‹ค.
  • ๋‹ค์ˆ˜์˜ ์˜ต์ €๋ฒ„ ๊ฐ์ฒด๋ฅผ ๋“ฑ๋ก ์ดํ›„ ํ•ด์ง€ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

์˜ˆ์ œ๋ฅผ ํ†ตํ•ด ์•Œ์•„๋ณด๋Š” Observer ํŒจํ„ด

 

๋‚ ์”จ API ๋กœ๋ถ€ํ„ฐ ์˜จ์Šต๋„ ์•Œ๋ฆผ ๋ฐ›๊ธฐ

ํ˜„์žฌ ๋‚ ์”จ์˜ ์˜จ๋„, ์Šต๋„, ๊ธฐ์•• ๋ฐ์ดํ„ฐ๋ฅผ ์–ป์„์ˆ˜ ์žˆ๋Š” ์„œ๋น„์Šค์ธ WeatherAPI ํด๋ž˜์Šค๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•˜์ž. ๊ฐ ๊ตญ๊ฐ€์˜ ์‚ฌ์šฉ์ž๋“ค์ด ์ด ๋‚ ์”จ API๋ฅผ ์ด์šฉํ•ด ์˜จ์Šต๋„ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€ display ํ•œ๋‹ค๋Š” ์„œ๋น„์Šค๋ฅผ ๊ฐœ๋ฐœํ•˜๋ ค๊ณ  ํ•œ๋‹ค.

 

ํด๋ฆฐํ•˜์ง€ ์•Š์€ ๋ฌธ์ œ์˜ ์ฝ”๋“œ โŒ

๊ทธ๋ž˜์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํด๋ž˜์Šค๋ฅผ ๊ตฌ์„ฑํ•˜์˜€๋‹ค. IUser ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ KoreanUser ํด๋ž˜์Šค์—์„œ๋Š” ์–ป์–ด์˜ค๋ ค๋Š” api์˜ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑ์ž๋กœ ๋ฐ›๊ณ  ์—ฐ๊ฒฐํ•˜์—ฌ ์‹ค์ œ ์˜จ์Šต๋„ ๋ฐ์ดํ„ฐ๋ฅผ ์ถœ๋ ฅํ•˜๋„๋ก ํ•˜์˜€๋‹ค.

Observer-pattern

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();

        // ...
    }
}

Observer-pattern

๋‹น์žฅ ๋™์ž‘์— ์žˆ์–ด์„œ ๋ฌธ์ œ๊ฐ€ ์—†์–ด๋ณด์ด์ง€๋งŒ, ํ•œ๋ˆˆ์— ์ฒ™ ๋ด๋„ ๋„ˆ๋ฌด Client ํด๋ž˜์Šค์— ์ฝ”๋“œ๊ฐ€ ์ง‘์•ฝ์ ์œผ๋กœ ๊ตฌ์„ฑ๋จ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ๋‚ ์”จ API๋ฅผ ์‹ ์ฒญํ•œ ์œ ์ €๋“ค์„ ๊ด€๋ฆฌํ•˜๋Š” ํ”„๋กœ์„ธ์Šค๋ฅผ ํด๋ผ์ด์–ธํŠธ์—์„œ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ์œ ์ €๋“ค์€ ๋‚ ์”จ ๋ฐ์ดํ„ฐ๊ฐ€ ๊ฐฑ์‹ ๋ ๋•Œ(measurementsChanged) ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌ๋ฐ›์•„ display ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ ์œ ์ € ํด๋ž˜์Šค๋‚ด์— ์žˆ๋Š” api ๊ฐ์ฒด์— ์ž์‹ ์ด ์ง์ ‘ ์กฐํšŒํ•˜์—ฌ display ํ•˜๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

์˜ต์ €๋ฒ„ ํŒจํ„ด์„ ์ ์šฉํ•œ ์ฝ”๋“œ โœ”๏ธ

๋”ฐ๋ผ์„œ ๋‚ ์”จ API๋ฅผ ์‹ ์ฒญํ•œ ์œ ์ €๋“ค์„ ๊ด€๋ฆฌํ•˜๋Š” ํ”„๋กœ์„ธ์Šค๋ฅผ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์•„๋‹Œ ๋‚ ์”จ API ํด๋ž˜์Šค์—์„œ ๊ด€๋ฆฌํ•˜๋„๋ก ์„ค์ •ํ•˜๊ณ , ๊ฐ ์œ ์ €๋“ค์ด ๋ณ€ํ™”๋œ ๋‚ ์”จ ๋ฐ์ดํ„ฐ๋ฅผ ์ž๋™์œผ๋กœ ์ „๋‹ฌ๋ฐ›์„ ์ˆ˜ ์žˆ๋„๋ก ์ผ์ข…์˜ ์ „ํŒŒ ๋ฐ ๋™์ž‘ ํ–‰์œ„๋ฅผ ๋‚ ์”จ API ํด๋ž˜์Šค์—์„œ ๊ตฌํ˜„ํ•  ํ•„์š”์„ฑ์ด ์žˆ๋‹ค. 

์ฆ‰, WeatherAPI ํด๋ž˜์Šค๋ฅผ ๋ฐœํ–‰์ž, ๊ด€์ฐฐ ๋Œ€์ƒ์ž๋กœ์„œ Subject ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋„๋ก ํ•˜๊ณ , API ์‚ฌ์šฉ์ž๋“ค์„ ๊ด€์ฐฐ์ž, ๊ตฌ๋…์ž ๋กœ์„œ Observer ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋„๋ก ๊ตฌ์„ฑํ•œ๋‹ค.

Observer-pattern

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-pattern

ํด๋ผ์ด์–ธํŠธ์—์„œ ๋ณ„๋‹ค๋ฅธ ๋ช…๋ น์—†์ด ์•Œ์•„์„œ ์ž๋™์œผ๋กœ ๋ฐœํ–‰์ž๋กœ๋ถ€ํ„ฐ ๊ตฌ๋…์ž๋“ค์ด ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌ๋ฐ›์•„ ์ ์ ˆํžˆ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ํ•ต์‹ฌ์€ ๊ตฌ๋…์ž๋“ค์˜ ๊ด€๋ฆฌ๋ฅผ ๋ฐœํ–‰์ž ๊ฐ์ฒด์—์„œ ๋ฆฌ์ŠคํŠธ๋กœ ํ•œ๋‹ค๋Š” ์ ๊ณผ ๋ฆฌ์ŠคํŠธ์— ์žˆ๋Š” ๋ชจ๋“  ๊ตฌ๋…์ž๋“ค์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์œ„์ž„ํ•˜์—ฌ ์‹คํ–‰ํ•จ์œผ๋กœ์จ ๋ณ€ํ™”๊ฐ€ ์žˆ์„๋•Œ๋งˆ๋‹ค ์ด๋ฒคํŠธ์„ฑ ์•Œ๋ฆผ์œผ๋กœ ์ „ํŒŒํ•œ๋‹ค๋Š” ์ปจ์…‰์œผ๋กœ์จ ์˜ต์ €๋ฒ„ ํŒจํ„ด์„ ์ดํ•ดํ•˜๋ฉด ๋œ๋‹ค.


์‹ค๋ฌด์—์„œ ์ฐพ์•„๋ณด๋Š” Observer ํŒจํ„ด

 

์ž๋ฐ”์˜ ๋‚ด์žฅ ์˜ต์ €๋ฒ„ ๊ฐ์ฒด

ํ”„๋ก์‹œ ๋””์ž์ธ ํŒจํ„ด์— ๋Œ€ํ•ด์„œ ์ž๋ฐ”๊ฐ€ ๋ณ„๋„๋กœ java.lang.reflect.Proxy ๋กœ ๋‚ด์žฅ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์ง€์›ํ•˜๋“ฏ์ด, ์˜ต์ €๋ฒ„ ํŒจํ„ด์„ ์ง์ ‘ ๊ตฌํ˜„ํ• ์ˆ˜๋„ ์žˆ์ง€๋งŒ ์ž๋ฐ”์—์„œ java.util.Observable(์ธํ„ฐํŽ˜์ด์Šค), java.util.Observer(ํด๋ž˜์Šค) ๋กœ ๋‚ด์žฅ ์˜ต์ €๋ฒ„ ๊ฐ์ฒด๋ฅผ ์ง€์›ํ•œ๋‹ค. ์ด ๋‚ด์žฅ ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•˜๋ฉด ์˜ต์ €๋ฒ„ ํŒจํ„ด์„ ์ง์ ‘ ๊ตฌํ˜„ํ•  ํ•„์š” ์—†์ด ๊ฐ„๋‹จํžˆ ํ•ด๋‹น ํด๋ž˜์Šค๋ฅผ ์ƒ์†ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ์˜ต์ €๋ฒ„ ๊ตฌ์กฐ๋ฅผ ์ด์šฉํ•  ์ˆ˜๊ฐ€ ์žˆ๋‹ค.

 

java.util.Observer

Observer ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ implements ํ•œ ํด๋ž˜์Šค๋Š” ์˜ต์ €๋ฒ„ ํด๋ž˜์Šค๊ฐ€ ๋˜๊ฒŒ ๋œ๋‹ค. ์˜ต์ €๋ฒ„ ํด๋ž˜์Šค๋“ค์€ update() ์ถ”์ƒ ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•จ์œผ๋กœ์จ, Observable ํด๋ž˜์Šค๋กœ ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์‹ ๋ฐ›๊ฒŒ ๋œ๋‹ค.

java.util.Observer

  • update(Observable o , Object arg) : Observable์—๊ฒŒ ์ „๋‹ฌ๋ฐ›์€ ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐฑ์‹ ํ•œ๋‹ค.

 

java.util.Observable

Observable ํด๋ž˜์Šค๋Š” ๋“ฑ๋ก๋œ ์˜ต์ €๋ฒ„๋“ค์„ ๊ด€๋ฆฌํ•˜๋ฉฐ, ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ค์–ด์˜ค๋ฉด ์˜ต์ €๋ฒ„์—๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๋ฐœํ–‰์ž ์—ญํ• ์„ ํ•œ๋‹ค. ์•„๋ž˜ ํด๋ž˜์Šค ๊ตฌํ˜„๋ถ€๋ฅผ ๋ณด๋ฉด Vector ์ปฌ๋ ‰์…˜์„ ํ†ตํ•ด ์˜ต์ €๋ฒ„๋“ค์„ ๊ด€๋ฆฌํ•˜๋Š”๊ฑธ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ Observable์˜ ๊ฐ€์žฅ ํฐ ํŠน์ง•์œผ๋กœ๋Š” ๋™๊ธฐํ™”(Synchronized)๊ฐ€ ๊ฑธ๋ ค์žˆ์–ด Thread-Safe ํ•˜๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ์ด Observable ํด๋ž˜์Šค๋ฅผ ์ƒ์†(extends)ํ•œ ํด๋ž˜์Šค๋Š” ๋ฐœํ–‰์ž ์—ญํ• ๋กœ์„œ ์ˆ˜ํ–‰ํ•˜๊ฒŒ ๋œ๋‹ค.

java.util.Observable

  • 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