[Reactive Java #1] 옵저버 패턴 사용해보기

YoungHo-Cha·2022년 10월 1일
1

Web Flux

목록 보기
2/6
post-thumbnail

본 코드는 아래 깃허브 주소에 있습니다.

✅ Git Repository


목차

  • 구현 상황 이해하기
  • Iterator
  • Observer

구현 상황 이해하기

먼저 1~10까지 출력하는 간단한 코드를 작성해야하는 상황이라고 생각해봅시다.

Iterator

Iterator를 이용하여 출력이 가능합니다. iterator를 이용해서 다양한 출력방법을 봅시다.

V1

public class BasicCodeV1 {

    public static void main(String[] args) {
        Iterable<Integer> iter = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        for(Integer value : iter){
            System.out.println(value);
        }
    }
}

위 코드는 간단히 리스트를 생성하여 향상 for문을 사용하여 출력한 내용입니다.

V2

public class BasicCodeV2 {

    public static void main(String[] args) {
        Iterable<Integer> iter1 = new Iterable<Integer>() {
            @Override
            public Iterator<Integer> iterator() {
                return new Iterator<Integer>() {
                    int i = 0;
                    final static int MAX_V1 = 10;
                    @Override
                    public boolean hasNext() {
                        return i < MAX_V1;
                    }

                    @Override
                    public Integer next() {
                        return ++i;
                    }
                };
            }
        };

        Iterable<Integer> iter2 = () ->{
            return new Iterator<>() {
                int i = 0;
                static final int MAX_V2 = 10;

                public boolean hasNext() {
                    return i < MAX_V2;
                }

                public Integer next() {
                    return ++i;
                }
            };
        };

        for(Integer value : iter1){
            System.out.println(value);
        }

        System.out.println("-------------- \n");

        for (Integer value : iter2){
            System.out.println(value);
        }
    }
}

위 코드는 Array를 직접 생성한 것이 아닌 내부 구현을 이용하여 구현한 내용입니다.

V3

public class BasicCodeV3 {
    public static void main(String[] args) {


        Iterable<Integer> iter = () ->{
            return new Iterator<>() {
                int i = 0;
                static final int MAX_V2 = 10;

                public boolean hasNext() {
                    return i < MAX_V2;
                }

                public Integer next() {
                    return ++i;
                }
            };
        };

        iter.forEach(v -> System.out.println(v));
    }
}

위 코드는 향상 for문이 아닌 forEach문을 사용하여 구현한 내용입니다.

Observer

위의 코드들을 다른방식으로 구현할 수 있습니다. Java에서 제공해주는 Observer 패턴을 이용하여 봅시다.

V1

@SuppressWarnings("deprecation")
public class Observable {

    static class IntObservable extends java.util.Observable implements Runnable{

        @Override
        public void run() {
            for (int i = 0; i <= 10; i++) {
                setChanged();
                notifyObservers(i);
            }
        }
    }
    public static void main(String[] args) {

        Observer ob = new Observer() {
            @Override
            public void update(java.util.Observable o, Object arg) {
                System.out.println("ob = " + arg);
            }
        };
        Observer ob2 = new Observer() {
            @Override
            public void update(java.util.Observable o, Object arg) {
                System.out.println("ob2 = " + arg);
            }
        };

        IntObservable io = new IntObservable();
        io.addObserver(ob);
        io.addObserver(ob2);


        io.run();
        System.out.println("옵저버 개수 = " + io.countObservers());
    }

}

해당 클래스는 옵저버 패턴을 지원해주기위한 java 라이브러리 입니다.

해당 코드의 실행 순서는 다음과 같습니다.

  1. Observer 객체 생성

  2. IntObservable 객체 생성

  3. Observer 객체를 IntObservable 객체에 등록

  4. IntObservable 객체 run

  5. IntObservable 객체 update 실행

  6. IntObservable 객체 update 메소드 내의 setChanged() 실행

  7. IntObservable 객체에 저장된 Observer 객체 notify 및 argument 전달

  8. Observer 객체 update 실행

  9. 4번 ~ 8번 반복

  10. 옵저버 개수 print

V2

@SuppressWarnings("deprecation")
public class ObservableV2 {

    static class IntObservable extends java.util.Observable implements Runnable{

        @Override
        public void run() {
            for (int i = 0; i <= 10; i++) {
                setChanged();
                notifyObservers(i);
            }
        }
    }
    public static void main(String[] args) {

        Observer ob = new Observer() {
            @Override
            public void update(java.util.Observable o, Object arg) {
                System.out.println("ob = " + arg);
            }
        };
        Observer ob2 = new Observer() {
            @Override
            public void update(java.util.Observable o, Object arg) {
                System.out.println("ob2 = " + arg);
            }
        };

        IntObservable io = new IntObservable();
        io.addObserver(ob);
        io.addObserver(ob2);


        ExecutorService es = Executors.newSingleThreadExecutor();
        es.execute(io);
        System.out.println("옵저버 개수 = " + io.countObservers());
        es.shutdown();
    }

}

해당 클래스의 실행은 다음과 같습니다.

  1. 옵저버 개수 print

  2. 반복을 통한 print 실행

V1의 코드와는 다른 점이 있습니다.

V2 코드는 "옵저버 개수"가 먼저 print 된 것을 볼 수 있습니다.

이유는 위의 코드는 별도의 Thread 에서 동작하였지만, 아래 코드는 single thread 에서 동작하였기 때문입니다.

요점

해당 객체에서 존재하는 내용과 basic code에서 존재하는 내용이 달라보이지만 개념면에서 push, pull 하는 부분은 똑같습니다.

옵저버 패턴은 스타크래프트의 옵저버와 같이 어떠한 객체를 쳐다보고 있는 상태입니다.

위의 코드에서 ob와 ob2는 IntObservable을 기다리는 상태(쳐다보는 상태)입니다.

결과적으로, 비동기적으로 처리한 모습을 볼 수 있었습니다.

한계

이러한 옵저버 패턴은 한계가 존재합니다.

  1. 데이터를 모두 주었다는 trigger 가 존재하지 않는다. -> 완료라는 개념이 존재하지 않는다.
  2. 에러를 대응하기가 힘듭니다.

다음 글 부터는 이러한 한계를 극복한 Reactive Streams에 대해서 알아봅시다.

참고

profile
관심많은 영호입니다. 궁금한 거 있으시면 다음 익명 카톡으로 말씀해주시면 가능한 도와드리겠습니다! https://open.kakao.com/o/sE6T84kf

0개의 댓글