Observer 패턴, 많이 들어봤다. GoF의 정의로 알아보자.

Observer

  • 관찰자, 감시자
  • 변화가 생기면 알아챈다.
  • 다만 이 관찰자는 여러명일 수 있다.

pub-sub Pattern

  • Observer보다 많이 쓰이는 패턴
  • 발행-구독 패턴
  • 비슷하나 엄밀히 말하면 다른 패턴이다.
  • 하지만 이루려는 목적은 비슷하여 같은 패턴이라 보는 일도 많다.
  • pub-sub이 보다 일반적인 (observer의 상위 집합) 개념이라 생각

LogManager

  • 바로 전에 봤던 LogManager가 그 패턴
  • program이 LogManager에게 로그 메시지를 보내고,
  • LogManager는 구독자(ConsoleLogger, EmailLogger)들에게 메시지를 보냄

Observer Pattern의 예

  • 크라우드펀딩
  • 돈이 들어올 때마다 두 개체를 업데이트할 것임
    • 장부 (금액)
    • 폰에서 푸시 노티 (이름, 금액)
      • 실제로 푸시 알림이 pub-sub 패턴임
      • 이를 이벤트 주도 아키텍처라고도 한다.
        • 이벤트가 일어나면 알려줌

IFundingCallback

public interface IFundingCallback {
    void onMoneyRaised(String backer, int amount);
}
  • 동일한 인터페이스로 받기 위해 만듦

BookkeepingApp

public final class BookkeepingApp implements IFundingCallback {

    // ... 생략

    @Override
    public void onMoneyRaised(String backer, int amount) {
        // 장부에 새 내역 추가
        // amount만 사용
    }
}

MobileApp

public final class MobileApp implements IFundingCallback {

    @Override
    public void onMoneyRaised(String backer, int amount) {
        // 모바일 앱에 알림 보여줌
        // amount, backer 둘다 사용
    }
}

CrowdFundingAccount

public final class CrowdFundingAccount {
    private int balance;

    private ArrayList<IFundingCallback> subscribers;

    public CrowdFundingAccount() {
        this.subscrivers = new ArrayList<IFundingCallback>();
    }

    public void subscribe(IFundingCallBack sub) {
        subscrivers.add(sub);
    }

    public void support(String backer, int amound) {
        this.balance += amount;

        for (IFundingCallback sub : subscribers) {
            sub.onMoneyRaised(backer, amount);
        }
    }
}

pub-sub과의 차이

  • 다대다인 관계라면 pub-sub이라 할 수 있다.
  • 지금 위의 경우 발행자가 하나다.
  • 그런데 만약 발행자가 여러개가 될 수 있고, 그걸 받는 쪽도 여러개라고 해보자.
  • 이럴 경우 그 중간에 조율해주는 개체가 있어야 한다.
  • 그 차이가 전부다. 위같은 경우는 Account에 직접적으로 빨대를 꽂고 있는 형태니 Observer이다.
  • pub-sub이라면, support()를 호출하면, 중간 관리 개체에 요청하고, 그 개체가 구독자들에게 다시 알림을 주는 형태가 될 것이다.
    • iOS의 NotificationCenter와 같은 방식이다.

Observer Pattern과 메모리 누수 문제

  • Observer Pattern은 결국 콜백 함수의 목록이다.
  • 그런데 이런 방식은 Managed 언어에서 메모리 누수를 만드는 주범이다.
    • 메모리 직접 관리
    • 가비지 컬렉터 기반 언어
      • 메모리 누수가 안생기는게 아님
      • 메모리 누수가 발생하는 방법이 다른 것
CrowdFundingAccount funding;

BookkeepingApp book = new BookkeepingApp();
funding.subscribe(book);

// 장부 앱으로 무언가 처리

// 다함, 이제 개체 지우자.

book = null;

// 시간이 많이 지남 - 가비지 컬렉터 한번 돎
// 하지만 안지워짐 응? 왜?
  • funding 개체가 해당 메모리 주소를 잡고 있기 때문에, 사용중이라고 생각함
    • subscribers
  • 명시적으로 unsubscribe()와 같은 동작으로 배열에서 빼줘야 함
public void unsubscribe(IFundingCallback sub) {
    subscribers.remove(sub);
}
  • 이거 안까먹을 자신 있음?...
  • 여기서 찾기도 굉장히 어렵다..
  • Java의 경우에는 book이 지워질 때 자동으로 unsubscribe()를 호출하게 만들기도 어렵다.
    • C++에는 destructor를 통해 자동화할 수 있다.
    • Swift deinit()
  • 또 C++은 굳이 heap에 안만들고 stack에 만들 수도 있다.
    • 이렇게 되면 함수 스코프 벗어나면 무조건적으로 메모리 할당 해제 시킬 수 있다.
      • stack이 그렇게 도니까!

Reference

profile
Goal, Plan, Execute.

0개의 댓글