본 코드는 아래 깃허브 주소에 있습니다.
먼저 1~10까지 출력하는 간단한 코드를 작성해야하는 상황이라고 생각해봅시다.
Iterator를 이용하여 출력이 가능합니다. iterator를 이용해서 다양한 출력방법을 봅시다.
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문을 사용하여 출력한 내용입니다.
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를 직접 생성한 것이 아닌 내부 구현을 이용하여 구현한 내용입니다.
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문을 사용하여 구현한 내용입니다.
위의 코드들을 다른방식으로 구현할 수 있습니다. Java에서 제공해주는 Observer 패턴을 이용하여 봅시다.
@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 라이브러리 입니다.
해당 코드의 실행 순서는 다음과 같습니다.
Observer 객체 생성
IntObservable 객체 생성
Observer 객체를 IntObservable 객체에 등록
IntObservable 객체 run
IntObservable 객체 update 실행
IntObservable 객체 update 메소드 내의 setChanged() 실행
IntObservable 객체에 저장된 Observer 객체 notify 및 argument 전달
Observer 객체 update 실행
4번 ~ 8번 반복
옵저버 개수 print
@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();
}
}
해당 클래스의 실행은 다음과 같습니다.
옵저버 개수 print
반복을 통한 print 실행
V1의 코드와는 다른 점이 있습니다.
V2 코드는 "옵저버 개수"가 먼저 print 된 것을 볼 수 있습니다.
이유는 위의 코드는 별도의 Thread 에서 동작하였지만, 아래 코드는 single thread 에서 동작하였기 때문입니다.
해당 객체에서 존재하는 내용과 basic code에서 존재하는 내용이 달라보이지만 개념면에서 push, pull 하는 부분은 똑같습니다.
옵저버 패턴은 스타크래프트의 옵저버와 같이 어떠한 객체를 쳐다보고 있는 상태입니다.
위의 코드에서 ob와 ob2는 IntObservable을 기다리는 상태(쳐다보는 상태)입니다.
결과적으로, 비동기적으로 처리한 모습을 볼 수 있었습니다.
이러한 옵저버 패턴은 한계가 존재합니다.
다음 글 부터는 이러한 한계를 극복한 Reactive Streams에 대해서 알아봅시다.