Observer Pattern

김영우 (AvocadoSmasher)·2022년 10월 24일
0

Design Pattern

목록 보기
2/8

🔎 What is Observer Pattern?

옵저버 패턴(Observer Pattern)은 한 객체의 상태의 변화 혹은 행위에 대하여 다른 객체들이 그에 따른 변화/행위를 인지하여야 하는 경우에 사용 가능한 패턴입니다.

제 나름대로의 말로 정리했을때는 위와 같은데 최대한 저의 입장에서 이해하기 쉬운 말로 표현했는데 의미전달이 잘 되었을지 의문이 남는군요. 위키백과에서는 다음과 같이 정의하고 있습니다.

옵서버 패턴
(observer pattern)은 객체의 상태 변화를 관찰하는 관찰자들, 즉 옵저버들의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버에게 통지하도록 하는 디자인 패턴
이다. 주로 분산 이벤트 핸들링 시스템을 구현하는 데 사용된다. 발행/구독 모델로 알려져 있기도 하다.

제가 본 옵저버 패턴은 기본적으로 관찰 대상관찰을 하는 객체(관찰자 라고 칭하겠습니다.) 이렇게 2가지 부류의 객체들이 존재하게 되고 관찰 대상관찰자 들에 대한 정보를 일반적으로 List의 형태로 가지게 됩니다. 해당 리스트에 관찰자 들을 삽입과 삭제를 하면서 관찰자 가 관찰을 할지 말지를 결정하고 관찰 대상 에서는 자신이 가지고 있는 List내의 관찰자 들에게만 메시지를 전달하는 그런 형태를 취합니다.

말이 길어지다 보니 직관적이지 않아 일반적으로 패턴에서 정의하는 함수, 변수들에 대한 정보를 알아봅시다.

  • 관찰 대상(Subject)
    • List(Java에서는 ArrayList를 주로 사용합니다.) : 관찰자 객체들을 참조합니다.
    • notify 함수 : notify 함수는 forEach 문을 통해서 list내의 관찰자들에게 메시지를 전달합니다.
    • DataObject : 관찰 대상 객체에서 특정 데이터에 대한 정보와 그에 관한 Observer 관리 메소드들만 처리할 수 있도록 하는 데이터.(Optional 한 부분이라고 생각합니다.)
  • 괄찰자(Observer)
    • update 함수 : 관찰 대상이 notify 함수에서 처리할 함수를 여기에 정의합니다.

⚓️ Digging into how it works.

예를 들어서 A씨에 가정에 대한 이야기를 해보도록 하죠. A씨의 가정에서는 한 달 생활비를 관리하는 공용의 통장이 있고 각자의 생활비 통장에서 필요할때 돈을 꺼내서 사용하는 형태라고 해보죠. 이 때 각 가족 구성원들은 통장에서 돈이 얼마 빠져나가서 현재 통장의 잔액에 대한 상태를 받는다고 해보죠. 이러한 상황에 대한 시뮬레이터를 구현할 때 어떻게 해야할까요??

Observer Pattern 에서는 이 상황에 대해서 통장을 Subject를 정의하고 각 가족 구성원들을 Observer 라고 정의한 후 이에 대한 상태 변화에 대해 notify를 통해 통보 받도록 하죠. 다음 클래스 다이어그램을 보도록 하죠.

🔎 What is Observer Pattern?

옵저버 패턴(Observer Pattern)은 한 객체의 상태의 변화 혹은 행위에 대하여 다른 객체들이 그에 따른 변화/행위를 인지하여야 하는 경우에 사용 가능한 패턴입니다.

제 나름대로의 말로 정리했을때는 위와 같은데 최대한 저의 입장에서 이해하기 쉬운 말로 표현했는데 의미전달이 잘 되었을지 의문이 남는군요. 위키백과에서는 다음과 같이 정의하고 있습니다.

옵서버 패턴
(observer pattern)은 객체의 상태 변화를 관찰하는 관찰자들, 즉 옵저버들의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버에게 통지하도록 하는 디자인 패턴
이다. 주로 분산 이벤트 핸들링 시스템을 구현하는 데 사용된다. 발행/구독 모델로 알려져 있기도 하다.

제가 본 옵저버 패턴은 기본적으로 관찰 대상관찰을 하는 객체(관찰자 라고 칭하겠습니다.) 이렇게 2가지 부류의 객체들이 존재하게 되고 관찰 대상관찰자 들에 대한 정보를 일반적으로 List의 형태로 가지게 됩니다. 해당 리스트에 관찰자 들을 삽입과 삭제를 하면서 관찰자 가 관찰을 할지 말지를 결정하고 관찰 대상 에서는 자신이 가지고 있는 List내의 관찰자 들에게만 메시지를 전달하는 그런 형태를 취합니다.

말이 길어지다 보니 직관적이지 않아 일반적으로 패턴에서 정의하는 함수, 변수들에 대한 정보를 알아봅시다.

  • 관찰 대상(Subject)
    • List(Java에서는 ArrayList를 주로 사용합니다.) : 관찰자 객체들을 참조합니다.
    • notify 함수 : notify 함수는 forEach 문을 통해서 list내의 관찰자들에게 메시지를 전달합니다.
    • DataObject : 관찰 대상 객체에서 특정 데이터에 대한 정보와 그에 관한 Observer 관리 메소드들만 처리할 수 있도록 하는 데이터.(Optional 한 부분이라고 생각합니다.)
  • 괄찰자(Observer)
    • update 함수 : 관찰 대상이 notify 함수에서 처리할 함수를 여기에 정의합니다.

⚓️ Digging into how it works.

예를 들어서 A씨에 가정에 대한 이야기를 해보도록 하죠. A씨의 가정에서는 한 달 생활비를 관리하는 공용의 통장이 있고 각자의 생활비 통장에서 필요할때 돈을 꺼내서 사용하는 형태라고 해보죠. 이 때 각 가족 구성원들은 통장에서 돈이 얼마 빠져나가서 현재 통장의 잔액에 대한 상태를 받는다고 해보죠. 이러한 상황에 대한 시뮬레이터를 구현할 때 어떻게 해야할까요??

Observer Pattern 에서는 이 상황에 대해서 통장을 Subject를 정의하고 각 가족 구성원들을 Observer 라고 정의한 후 이에 대한 상태 변화에 대해 notify를 통해 통보 받도록 하죠. 다음 클래스 다이어그램을 보도록 하죠.

(출처 : 위키백과 옵서버 패턴 페이지.)

여기서 Observer는 interface 혹은 abstract의 형태일 것이고 subject는 통장이며 notifyObservers() 메소드는 각 Observer들이 구현한 통장 잔고를 받는 메소드를 호출하며 메시지를 전달할 것입니다.

💻 Code (feat. Java)

이번에는 java로 작성된 코드로 바로 위에서 언급한 예시를 보도록 하겠습니다.

BudgetObserver interface

public interface BudgetObserver {
    void notify(int current_balance);
    void withDraw(int amount);
}

Person class

public class Person implements BudgetObserver{
    int my_money = 0;
    int total_balance;
    @Override
    public void notify(int current_balance) {
        total_balance = current_balance;
    }

    @Override
    public void withDraw(int amount) {
        my_money += amount;
    }
}

FamilyBudget class

import java.util.ArrayList;

public class FamilyBudget {
    int total_balance;
    ArrayList<BudgetObserver> observers = new ArrayList<>();

    public void addObserver(BudgetObserver observer){
        observers.add(observer);
    }

    public void removeObserver(BudgetObserver observer){
        observers.remove(observer);
    }
    public void withDraw(BudgetObserver observer, int amount){
        observer.withDraw(amount);
        notifyAllObservers();
    }

    public void notifyAllObservers(){
        for(BudgetObserver observer : observers){
            observer.notify(total_balance);
        }
    }
}

이렇게 되면 각 Person이 돈을 꺼내면 FamilyBudget 객체를 통해 누군가 돈을 꺼내면 이제 각 observer(Person)에게 돈이 얼마가 남았는지 전달해 줄 수 있겠죠?

더 나아가 BudgetObserver를 implement 할 DataObject를 abstract class를 만들고 Person이 이를 Extend하는 형태로 구현한다면 Person이 뭐 돈 관리 외에도 다양한 일을 할텐데 이와 같이 돈의 관리를 Person에게 포함시키는 것이 아니라 확장하는 형태를 취한다면 더 OOP의 형태로 구현이 가능하겠죠??

📌 Summary

이번에는 Observer Pattern을 구현해 보았습니다. 옵저버 패턴은 기본적으로 관찰자(Observer)와 관찰 대상(Subject)로 구분하여 Subject 측에서 Observer들에 대한 List를 통해 연결관계를 유지하고 notifyAll를 통해 list내의 observer들에게 상태 변화에 대한 메시지를 전달하는 형태라는 것을 알았습니다.

사실 제가 캡스톤 프로젝트를 할 때 Java와 Kotlin을 사용하여 Android 앱 개발시에 Observer 패턴과 같은 형태로 Event의 처리 보단 Listener 형태를 상당히 많이 봤던 기억이 있습니다. 하지만 기본적으로 이런 접근법이 있다 라는 사실을 알고 간다는 측면에서 좋은 경험이 된것 같습니다.

  • P.S ) 이러한 옵저버 패턴은 Java에서 라이브러리의 형태로 제공하지만 이는 현재 Deprecated 되어 Listener를 사용하기를 권장하는 것 같습니다.

(출처 : 위키백과 옵서버 패턴 페이지.)

여기서 Observer는 interface 혹은 abstract의 형태일 것이고 subject는 통장이며 notifyObservers() 메소드는 각 Observer들이 구현한 통장 잔고를 받는 메소드를 호출하며 메시지를 전달할 것입니다.

💻 Code (feat. Java)

이번에는 java로 작성된 코드로 바로 위에서 언급한 예시를 보도록 하겠습니다.

BudgetObserver interface

public interface BudgetObserver {
    void notify(int current_balance);
    void withDraw(int amount);
}

Person class

public class Person implements BudgetObserver{
    int my_money = 0;
    int total_balance;
    @Override
    public void notify(int current_balance) {
        total_balance = current_balance;
    }

    @Override
    public void withDraw(int amount) {
        my_money += amount;
    }
}

FamilyBudget class

import java.util.ArrayList;

public class FamilyBudget {
    int total_balance;
    ArrayList<BudgetObserver> observers = new ArrayList<>();

    public void addObserver(BudgetObserver observer){
        observers.add(observer);
    }

    public void removeObserver(BudgetObserver observer){
        observers.remove(observer);
    }
    public void withDraw(BudgetObserver observer, int amount){
        observer.withDraw(amount);
        notifyAllObservers();
    }

    public void notifyAllObservers(){
        for(BudgetObserver observer : observers){
            observer.notify(total_balance);
        }
    }
}

이렇게 되면 각 Person이 돈을 꺼내면 FamilyBudget 객체를 통해 누군가 돈을 꺼내면 이제 각 observer(Person)에게 돈이 얼마가 남았는지 전달해 줄 수 있겠죠?

더 나아가 BudgetObserver를 implement 할 DataObject를 abstract class를 만들고 Person이 이를 Extend하는 형태로 구현한다면 Person이 뭐 돈 관리 외에도 다양한 일을 할텐데 이와 같이 돈의 관리를 Person에게 포함시키는 것이 아니라 확장하는 형태를 취한다면 더 OOP의 형태로 구현이 가능하겠죠??

📌 Summary

이번에는 Observer Pattern을 구현해 보았습니다. 옵저버 패턴은 기본적으로 관찰자(Observer)와 관찰 대상(Subject)로 구분하여 Subject 측에서 Observer들에 대한 List를 통해 연결관계를 유지하고 notifyAll를 통해 list내의 observer들에게 상태 변화에 대한 메시지를 전달하는 형태라는 것을 알았습니다.

사실 제가 캡스톤 프로젝트를 할 때 Java와 Kotlin을 사용하여 Android 앱 개발시에 Observer 패턴과 같은 형태로 Event의 처리 보단 Listener 형태를 상당히 많이 봤던 기억이 있습니다. 하지만 기본적으로 이런 접근법이 있다 라는 사실을 알고 간다는 측면에서 좋은 경험이 된것 같습니다.

  • P.S ) 이러한 옵저버 패턴은 Java에서 라이브러리의 형태로 제공하지만 이는 현재 Deprecated 되어 Listener를 사용하기를 권장하는 것 같습니다.
profile
Android Studio 공부 중

0개의 댓글