public interface Observer {
public abstract void update(NumberGenerator generator);
}
Observer 인터페이스는 관찰자를 표현하는 인터페이스이고 구체적인 관찰자는 이 인터페이스를 구현함
update 메소드는 NumberGenerator 클래스에서 호출됨, 해당 클래스에서 나의 내용이 갱신되어 표시 쪽도 갱신해 주라는 것을 Observer에 전달하기 위한 메소드임
import java.util.ArrayList;
import java.util.Iterator;
public abstract class NumberGenerator {
private ArrayList observers = new ArrayList(); // Observer를 저장
public void addObserver(Observer observer) { // Observer를 추가
observers.add(observer);
}
public void deleteObserver(Observer observer) { // Observer를 삭제
observers.remove(observer);
}
public void notifyObservers() { // Observer에 알림
Iterator it = observers.iterator();
while (it.hasNext()) {
Observer o = (Observer)it.next();
o.update(this);
}
}
public abstract int getNumber(); // 수를 취득함
public abstract void execute(); // 수를 생성함
}
NumberGenerator 클래스는 수를 생성하는 추상 클래스임, 실제의 수의 생성(execute 메소드)과 수를 취득하는 부분(getNumber 메소드)은 하위 클래스에서 구현하도록 추상 메소드로 되어 있음
observers 필드는 NumberGenerator를 관찰하는 Observer를 보존하는 필드임
addObserver는 Observer를 추가하는 메소드이고, deleteObserver는 Observer를 삭제하는 메소드임
notifyObservers 메소드는 Observer 전원에 대해서 나의 내용이 갱신되었으므로 당신의 표시를 갱신해 주십시오라고 전함, 이 메소드 안에서는 observers 안의 Observer들 한 사람 한 사람의 update 메소드를 호출함
import java.util.Random;
public class RandomNumberGenerator extends NumberGenerator {
private Random random = new Random(); // 난수발생기
private int number; // 현재의 수
public int getNumber() { // 수를 취득함
return number;
}
public void execute() {
for (int i = 0; i < 20; i++) {
number = random.nextInt(50);
notifyObservers();
}
}
}
NumberGenerator의 하위 클래스이고, 난수를 생성함
random 필드에는 java.util.Random 클래스의 인스턴스가 저장되고, number 필드에는 현재의 난수값이 저장됨, getNumber 메소드는 number 필드의 값을 반환함
execute 메소드는 난수를 20개 생성하고, 그때마다 notifyObservers를 사용해서 관찰자에게 통지함
nextInt 메소드는 랜덤인 정수값을 반환함
public class DigitObserver implements Observer {
public void update(NumberGenerator generator) {
System.out.println("DigitObserver:" + generator.getNumber());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}
Observer 인터페이스를 구현하는 클래스로 관찰한 수를 숫자로 표시하기 위한 것임
update 메소드 안에서 인수로 주어진 NubmerGenerator의 getNumber 메소드를 사용해서 수를 취득하고 그것을 표시함(표시되는 모습을 잘 보이도록 Thread 사용)
public class GraphObserver implements Observer {
public void update(NumberGenerator generator) {
System.out.print("GraphObserver:");
int count = generator.getNumber();
for (int i = 0; i < count; i++) {
System.out.print("*");
}
System.out.println("");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}
public class Main {
public static void main(String[] args) {
NumberGenerator generator = new RandomNumberGenerator();
Observer observer1 = new DigitObserver();
Observer observer2 = new GraphObserver();
generator.addObserver(observer1);
generator.addObserver(observer2);
generator.execute();
}
}
RandomNumberGenerator의 인스턴스를 한 개 만들고, 그 관찰자를 두 개 만듬
observer1은 DigitObserver, observer2는 GraphObserver의 인스턴스임
addObserver 메소드를 사용해서 관찰자를 등록한 후 generator.execute를 사용해서 수를 생성함
Subject는 관찰되는 대상을 나타냄
관찰자인 Observer 역할을 등록하는 메소드와 삭제하는 메소드를 가지고 있음
현재의 상태를 취득하는 메소드도 선언되어 있음
ConcreteSubject는 구체적으로 관찰되는 대상을 나타냄
상태가 변화하면 그것이 등록되어 있는 Observer 역할에 전함
이 패턴 역시 클래스를 재이용 가능한 부품으로 만들 수 있음
Observer 패턴에서는 상태를 가지고 있는 ConcreteSubject 역할과 상태변화를 전달 받는 ConcreteObserver 역할이 등장함, 이 두 가지 역할을 연결하는 것이 인터페이스(API)인 Subject 역할과 Observer 역할임
RandomNumberGenerator 클래스는 현재 자신을 관찰하고 있는 것이 DigitObserver 클래스의 인스턴스인지 GraphObserver 클래스의 인스턴스인지 몰라도 상관없음, 그러나 observer 필드에 저장되어 있는 인스턴스들이 Observer 인터페이스를 구현하고 있다는 것을 알고 있음
이 인스턴스들은 addObserver에서 추가된 것이므로 반드시 Observer 인터페이스를 구현하고 있으며 update 메소드를 호출할 수 있음
그리고 DigitObserver 클래스는 자신이 관찰하고 있는 것이 RandomNumberGenerator 클래스의 인스턴스인지 다른 클래스의 인스턴스인지 신경 쓰지 않음, 단지 NumberGenertator의 하위 클래스의 인스턴스이고 getNumber 메소드를 가지고 있다는 것을 알고 있음
여기서 반복적으로 나오는 것이 2가지 존재함
NumberGenerator는 update 메소드를 사용해서 갱신되었다고 Observer에게 통지하고 있음, update 메소드의 인수로 주어지고 있는 것은 호출을 행한 NumberGenerator의 인스턴스 뿐임
Observer는 update 메소드 안에서 getNumber를 호출해서 필요한 정보를 얻어야함
예제 프로그램 한정해서 update 메소드의 인수로 갱신된 수 그 자체를 제공해도 상관없음
원래 update 메소드
void update(NumberGenerator generator);
수 자체 제공
void update(NumberGenerator generator, int number);
더 단순하게
void update(int number);
Observer는 능동적으로 관찰하는 것이 아니고, Subject 역할로부터 전달되는 것을 수동적으로 기다리고 있음
그래서 이를 Publish-Subscribe 패턴이라고도 함, publish(발행)와 subscribe(구독) 정도
MVC 안에 Model과 View의 관계는 Observer 패턴의 Subject 역할과 Observer 역할의 관계에 대응함
Model은 표시 형식에 의존하지 않는 내부 모델을 조작하는 부분임
또 View는 Model이 어떻게 보일 것인지를 관리하고 있는 부분임
일반적으로 하나의 Model에 복수의 View가 대응함
LiveData와 ViewModel 자체가 Observer 패턴을 활용해서 쓸 수 있는 방식임
앱에서는 데이터의 변화가 끊임없이 일어나는데 이때 마다 앱의 UI(Activity나 Fragment등)를 갱신시켜야 하는데 이때 LiveData를 사용하여서 UI를 자동으로 갱신해 줄 수 있음
즉 위의 예시에서 LiveData가 Observer 역할을 함, 각각 View에 대해서 마치 Observer에서 update를 통해서 해당 클래스 내용을 갱신해주듯이 ViewModel에서 LiveData를 관찰하는 것임
이렇게 하면 UI 즉 Activity등 Fragment 등에서 원래 기존 방식은 UI와 Data가 종속된 형태로 Activity와 Fragment 내부에서 처리를 했지만 이를 분리시켜 View는 View만을 표현하는 로직을 실질적인 데이터는 이 ViewModel을 통해서 LiveData로 update를 하면서 해당 UI에 맞게 갱신을 해주는 것이 가능해짐
이를 구조화 시키면 아래와 같음
직접적인 예시를 써본다면 만약 3개의 다른 Fragment 화면에 변화를 Observe하고 이것이 같은 Activity에서 보여지는 것이고 같은 ViewModel을 쓴다고 할 때 아래처럼 예시를 들면
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() {
return selected;
}
}
public class ListFragment extends Fragment {
private SharedViewModel model;
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
model.getSelected().observe(getViewLifecycleOwner(), item -> {
// Update the UI.
});
}
}
이는 마치 Subject에서 Observer로 통지하듯이 ViewModel에 의해서 Fragment에서 데이터를 통지하고 이런 처리를 LiveData가 Observer에서 update하듯이 처리할 수 있는 것임
즉 ViewModel이 Observer의 역할을 그리고 Activity나 Fragment 같은 View가 Subject의 역할을 하여서 해당 데이터를 View가 직접 처리하지 않고 LiveData를 통해서 Observe 할 수 있는 것임
이 방식이 생소할 수 있지만 기본적으로는 그냥 막연하게 UI단에 모든 것을 부여해서 썼지만 이 방식을 통해서 그런 비효율성을 개선할 수 있음
이는 그리고 RxAndroid, Reactive Android 상에서 이런 패턴을 적용해서 활용이 가능함, 그 동작은 위에서 서술한 것과 유사하게 이루어짐(Reactive하게 반응한다는 것 자체가 Observer 패턴에 의의를 생각해보면 어느정도 일치하는 바가 있는 부분임)