학습일지/Language
[Design Pattern] Observer
inspirit941
2020. 12. 17. 07:06
반응형
Observer 패턴
한 객체의 상태가 바뀌면, 해당 객체에 의존하는 다른 모든 객체에게 notice 전송 + 자동으로 내용이 갱신되는 one to many 방식의 의존성을 정의한 객체.
Subject 객체의 bCheck 변수 - 해당 객체가 변경되었는지 여부를 알리는 boolean.
- bCheck가 false이면, Subject 객체에 의존하는 Observer 객체에게 값 변경이 있음을 알린다.
- Observer 객체는 update() 메소드로 Subject값이 어떻게 변경되었는지 확인하고, 변경된 값에 대응되는 메소드를 실행한다.
자바의 Observer 내장패턴 사용하기
Push방식, Pull방식 모두 사용가능함.
java.util.Observer 인터페이스 구현 후 Observable 객체의
- addObserver() : Observer 목록에 추가.
- deleteObserver() : Observer 목록에서 삭제.
java.util.Observable을 상속받은 클래스에서 setChanged() 메소드 호출 시, 객체의 상태가 바뀌었음을 세팅
- notifyObservers() / notifyObserver(Object arg) 호출.
Observer 객체는 update(Observable o, Object arg) 메소드를 구현하는 걸로 변경안내를 받을 수 있다.
- Observable는 연락을 보낸 객체
- Object arg는 notifyObserver(Object arg)에서 파라미터로 정의된 객체값임.
// Observable 객체. 상태 변화가 발생할 경우 Observer에 알림을 보내는 객체.
public class PlayController extends Observable {
private boolean bPlay; // 실행여부
public PlayController () {
}
// 데이터를 전달받아 플래그값을 변경하고,
// 데이터가 변경되었음을 Observer에게 알린다.
public void setFlag(boolean bPlay) {
this.bPlay = bPlay;
// 값 변경 = 아래 두 개의 메소드 실행하면 됨.
// 별도로 구현할 내용 없음.
setChanged()
notifyObservers();
}
public boolean getFlag() {
return bPlay;
}
}
// Observer 객체 생성
public class Playable implements Observer {
// Observer로 등록할 객체
Observable observable;
private boolean bPlay;
public Playable(Observable o) {
// 생성 시 Observable 변수에 객체 할당
this.observable = o;
// 자신을 observer로 등록.
observable.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
(if o instanceof PlayController) {
PlayController controller = (PlayController) o;
this.bPlay = controller.getFlag();
// 필요한 로직 수행.
doAction();
}
}
public void doAction() {
if (bPlay) System.out.println("프로그램 시작");
else System.out.println("프로그램 종료");
}
}
public class TestObservationPattern {
public static void main(String[] args){
PlayController controller = new PlayController();
Playable observer = new Playable(controller);
System.out.println("클래스 일시정지");
controller.setFlag(false); // observer에서 프로그램 종료를 콘솔창에 반환
System.out.println("클래스 재시작");
controller.setFlag(true); // observer에서 프로그램 시작을 콘솔창에 반환
}
}
cf. Observable 객체는 JAVA 9부터 Deprecated 되었음.
문제점
- 재사용성.
- Observable은 클래스로 정의되어 있음. 즉 subclass 형태로 구현해야 하는데, 이미 다른 클래스를 상속받은 상태라면 Observable 객체를 상속받을 수 없는 구조.
- Observable의 핵심 클래스를 외부에서 호출할 수 없다.
- setChanged() 메소드가 protected로 정의되어 있음. 즉 Observable을 상속받은 서브클래스만 setChanged()를 호출할 수 있다.
- Observable 클래스는 Serialize가 불가능하다. Observable 클래스는 Serializer 인터페이스의 구현체가 아니기 때문
- 모든 Observer의 구현로직이 동일하다. instanceof로 Observable 객체를 확인하고, 해당 객체의 타입으로 cast하는 방식의 update메소드.
- Not Thread Safe. notification can occur in different orders / on different threads.
- 너무나도 제한된 쓰임새. (Not provide a rich Enough event model for applcation). 무언가가 바뀌었다는 사실은 전달하지만, 어떤 게 바뀌었는지는 전달하지 못하는 객체 구조
java.beans 패키지의 Listener를 사용하라고 권장하고 있다.
PropertyChangeListener나 PropertyChangeEvent와 같은...
직접 구현할 경우
public interface Publisher {
public void addObserver(Observer o);
public void deleteObserver(Observer o);
public void notifyObserver();
}
public interface Observer {
public void update(boolean play);
}
public class PlayController implements Publisher {
private List<Observer> observers = new ArrayList<>();
private boolean play;
@Override
public void addObserver(Observer observer) { observers.add(observer) }
@Override
public void deleteObserver(Observer observer) {
// 해시맵 쓰면 더 효율적일 거 같은데
int index = observer.indexOf(observer);
observers.remove(index);
}
@Override
public void notifyObservers() {
for (int i=0; i < observers.size(); i++) {
observers.get(i).update(play);
}
}
public void setFlag(boolean play) {
this.play = play;
notifyObservers();
}
public void getFlag() { return play; }
}
public class Playable implements Observer {
private boolean bPlay;
// Publisher를 생성자에 파라미터로 추가해서, 객체 생성 시 바로 등록되도록 만들 수도 있다.
@Override
public void update(boolean play) {
this.bPlay = bPlay;
doAction();
}
public void doAction() {
if (bPlay) System.out.println("프로그램 시작");
else System.out.println("프로그램 종료");
}
}
public class TestObservationPattern {
public static void main(String[] args){
PlayController pager = new PlayController();
Playable observer = new Playable(controller);
// 등록
pager.addObserver(observer);
// 메시지 등록
pager.setFlag(false); // 콘솔에 "프로그램 종료" 출력
// 삭제
pager.deleteObserver(observer);
// 메시지 등록
pager.setFlag(true);
}
}
안드로이드의 View / Button 등, 위젯의 이벤트를 받을 때 주로 쓰임.
- 버튼에는 클릭 이벤트가 OnClickListener 인터페이스로 구성돼 있다. 즉 버튼이라는 객체는 Publisher, OnClickListener는 Observer가 됨. 클릭이라는 상태 변경이 발생할 경우 OnClickListener로 알려주는 것
반응형