Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

옵저버 패턴이 필요할 때 Flow를 사용해도 되는가?(이장안) #186

Open
lja3723 opened this issue Aug 7, 2024 · 0 comments

Comments

@lja3723
Copy link
Collaborator

lja3723 commented Aug 7, 2024

문제가 무엇인가?

옵저버 패턴이 필요할 때 Flow를 사용해도 되는가?

왜 이런 문제를 선정하였는가?

Flow API를 볼 때마다 디자인패턴의 Observer 패턴과 정말 유사하다고 느꼈다. 그러면 개발할 때 옵저버 패턴이 필요할 때마다 Flow 인터페이스를 이용하여 개발해도 되는 것인가?

자신이 생각한 답변은 무엇인가?

Flow와 옵저버 패턴의 공통점과 차이점을 먼저 알아보았다.

공통점

1. 관계 구조

  • 옵저버 패턴: Observer <-> Subject 간 일대다 관계를 정의한다. Subject가 상태를 변경할 때 Observer에게 알린다.
  • Flow API: Publisher <-> Subscriber 간의 일대다 관계를 정의한다. Publisher가 데이터를 발행하면 Subscriber에게 전달한다.

2. 이벤트 기반 통신

두 방식 모두 이벤트 기반 통신을 통해 상태 변화나 데이터 발생을 Observer(또는 Subscriber)에게 알린다.

3. 구독 관리

  • 옵저버 패턴: Observer는 Subject에게 등록 및 해제할 수 있다.
  • Flow API: Subscriber는 Publisher에게 구독 및 취소할 수 있다.

차이점

1. Backpressure(역압력)

  • 옵저버 패턴: 기본적으로 역압력 개념이 없다. Subject가 이벤트를 발생시키면 모든 옵저버에게 즉시 전달된다.
  • Flow API: Subscriber는 자신이 처리할 수 있는 데이터 양을 Publisher에게 요청할 수 있고 Publisher는 요청된 데이터 양에 맞게 데이터를 발행한다. 이로써 시스템 과부화 방지하는 역압력을 구현한다.

2. 비동기 처리

  • 옵저버 패턴: 일반적으로 동기식으로 동작한다. Subject가 상태를 변경하면 모든 옵저버에게 즉시 알립니다.
  • Flow API: 비동기 처리를 염두에 두고 설계되어 Publisher와 Subscriber는 서로 다른 스레드에서 동작할 수 있으며, 비동기 데이터 스트림을 처리한다.

결론

Flow API는 비동기 데이터 스트림 처리를 위해 제공되었지만 옵저버 패턴을 구현에도 충분히 적절하게 사용할 수 있다. 아래는 옵저버 패턴을 Flow API를 이용하여 구현한 것이다.

package modern.ch17;

import java.util.concurrent.Flow;
import java.util.concurrent.SubmissionPublisher;

// 피관찰자 (Subject) 역할을 하는 Publisher
class SubjectPublisher extends SubmissionPublisher<String> {
    // 필요시 추가적인 구현 가능
}

// 옵저버 (Observer) 역할을 하는 Subscriber
class ObserverSubscriber implements Flow.Subscriber<String> {
    private String name;
    private Flow.Subscription subscription;

    public ObserverSubscriber(String name) {
        this.name = name;
    }

    @Override
    public void onSubscribe(Flow.Subscription subscription) {
        this.subscription = subscription;
        subscription.request(1); // 처음 아이템 하나 요청
    }

    @Override
    public void onNext(String item) {
        System.out.println(name + " received: " + item);
        subscription.request(1); // 다음 아이템 하나 요청
    }

    @Override
    public void onError(Throwable throwable) {
        throwable.printStackTrace();
    }

    @Override
    public void onComplete() {
        System.out.println(name + " is done");
    }
}

// Flow로 구현한 옵저버 패턴 테스트
public class FlowObserverExample {
    public static void main(String[] args) throws InterruptedException {
        // 피관찰자 (Publisher) 생성
        SubjectPublisher publisher = new SubjectPublisher();

        // 옵저버 (Subscriber) 생성 및 구독
        ObserverSubscriber observer1 = new ObserverSubscriber("Observer1");
        ObserverSubscriber observer2 = new ObserverSubscriber("Observer2");

        publisher.subscribe(observer1);
        publisher.subscribe(observer2);

        // 데이터 전송
        System.out.println("Publishing items...");
        publisher.submit("Hello");
        publisher.submit("World");

        // Publisher 종료
        publisher.close();

        // 잠시 대기하여 비동기 작업 완료 대기
        Thread.sleep(1000);
    }
}

아래는 실행 결과이다.

Publishing items...
Observer1 received: Hello
Observer2 received: Hello
Observer1 received: World
Observer2 received: World
Observer2 is done
Observer1 is done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant