본문 바로가기

Develop/Design Pattern

[Design Pattern] 옵저버 (Observer) 패턴

728x90

1. 개요

  • 옵저버 디자인 패턴(Observer Design Pattern)은 관찰자 observer 가 관찰대상 subject상태변화가 있을 때마다 관찰자에게 알리고, 알림을 받은 관찰자가 행동하도록 하는 패턴이다.

2. 상황

  • 한 개의 관찰 대상자 subject 가 여러개의 관찰자 observer 를 가질 때

3. 다이어그램

observer-default

3.1. 설명

  • Subject
    • Observer에 대한 등록, 해제, 알림 전송 등을 관리하는 역할을 수행한다.
    • 일반적으로 하나 이상의 Observer를 관리한다.
    • 상태가 변경될 때마다 등록된 모든 Observer에게 상태 변화를 알린다.
  • Observer
    • Subject의 상태 변화에 대한 알림을 수신하고 처리하는 역할을 수행한다.
    • Subject에서 발생한 알림을 받은 후 필요한 작업을 수행하거나 상태를 업데이트한다.

4. 구현

public interface Observer {
  public void update();
}

class ObserverA implements Observer {
  @Override
  public void update() {
    System.out.println("ObserverA: Received an update.");
  }
}

class ObserverB implements Observer {
  @Override
  public void update() {
    System.out.println("ObserverB: Received an update.");
  }
}
public interface Subject {
  public void addObs(Observer o);
  public void removeObs(Observer o);
  public void notifyObs();
}

public class ConcreteSubject implements Subject {
  private List<Observer> observers = new ArrayList<>();

  @Override
  public void addObs(Observer o) { observers.add(o); }
  @Override
  public void removeObs(Observer o) { observers.remove(o); }
  @Override
  public void notifyObs() { 
    System.out.println("ConcreteSubject: Notifying observers...");
    for (Observer o : observers) o.update();
  }
}
public class Client {
  public static void main(String[] args) {
    Subject s = new ConcreteSubject();

    Observer oA = new ObserverA();
    Observer oB = new ObserverB();

    s.addObs(oA);
    s.addObs(oB);
    s.notifyObs();

    s.removeObs(oA);
    s.notifyObs();
  }
}

5. 예시

public interface Observer {
  void update(Product p);
}

class UserObserver implements Observer {
  private String username;

  public UserObserver(String username) {
    this.username = username;
  }

  @Override
  public void update(Product p) {
    System.out.println(username + "님, " + p.getName() + "이 할인 중 입니다.");
    System.out.println(p.getPrice() + "원 (" + 100 * p.getDiscountRate() + "% 할인)");
  }
}
public abstract class Subject {
  private List<Observer> observers = new ArrayList<>();

  public void addO(Observer o) { observers.add(o); }
  public void removeO(Observer o) { observers.remove(o); }
  public void notifyO(Product p) { for (Observer o : observers) o.update(p); }
}

public class Product extends Subject {
  private String name;
  private int price;
  private double discountRate;

  public String getName() { return name; }
  public int getPrice() { return price; }
  public double getDiscountRate() { return discountRate; }

  public Product(String name, int price) {
    this.name = name;
    this.price = price;
    discountRate = .0;
  }

  public void discount(double rate) { 
    discountRate = rate;
    price *= (1 - rate); 
    notifyO(this);
  }
}
public class Client {
  public static void main(String[] args) {

    Product p = new Product("Macbook", 1_000_000);

    Observer james = new UserObserver("James");
    Observer jane = new UserObserver("Jane");

    p.addO(james);
    p.addO(jane);

    p.discount(.2);

    p.removeO(james);

    p.discount(.3);
  }
}

실행결과

James님, Macbook이 할인 중 입니다.
800000원 (20.0% 할인)
Jane님, Macbook이 할인 중 입니다.
800000원 (20.0% 할인)
Jane님, Macbook이 할인 중 입니다.
560000원 (30.0% 할인)

6. 장단점

6.1. 장점

  • 관찰 대상 subject상태 변경을 자동으로 감지할 수 있다.
  • OCP를 준수한다.

6.2. 단점

  • 코드 복잡도가 증가한다.
  • observer 객체를 해지하지 않으면 메모리 누수가 발생할 수 있다.

관련 포스팅

728x90