2019. 9. 12. 21:48ㆍ카테고리 없음
옵저버 패턴
옵저버 패턴은 한 객체의 상태가 변경이 되면 그 객체에 의존하고 있는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다( one to many ) 의존성을 가진다.
대부분의 옵저버 패턴이 위와 같은 형태를 가진다. 구독을 하는 옵저버들에게 새로 갱신되는 데이터를 전달해줄 주체가 되는 Subject 객체가 있고, Subject 객체는 새로 변경되는 내용이 생기면 해당 내용들을 옵저버들에게 전달해준다. 옵저버는 새로 추가될 수도 있고, 옵저버 상태에서 벗어날 수 있다. 더 이상 옵저버가 아닌 상태의 객체는 Subject의 변경사항을 제공받지 못한다.
가수의 앨범 발매 소식이 필요해!
가수의 회사에서는 최근 작업 앨범과 발매 예정일, 작업 상태를 가지고 있다. 이 정보가 변경될 때마다 구독한 팬들에게 알려주고 싶다.
interface SingerSubject {
public void addObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObserver();
}
class PostMalone implements SingerSubject{
private String albumName;
private Date releaseDate;
private float state;
private List<Observer> observers;
public void addObserver(Observer o){...}
public void removeObserver(Observer o){...}
public void notifyObserver(){...}
...
}
기본적으로 옵저버 패턴에서 Subject 클래스에서는 위와 같이 가수의 앨범 소식을 구독한 팬을 추가하는 add함수와 삭제하는 remove함수 바뀐 정보를 전달해주는 notify함수가 있다.
이제 구독자인 옵저버를 만들어 보자. 먼저 구독자는 새로운 정보를 받을 수 있도록 정보를 update 받는 함수를 가지고 있어야 한다. 먼저 구독자들 중에는 앨범 소식을 기다리는 Fan과 앨범 소식을 가지고 기사를 써야 하는 기자, 앨범 소식을 가져다 자신의 음악 사이트에 사용하려는 개발자 등등 서로 다 쓰임이 다르다. 기존에 스트래티지 패턴에서 배운 대로 옵저버 클래스는 각각이 동작이 다르고 서로 변경되거나 수정이 될 때 서로에게 영향을 주지 않도록 구현해야 한다.
interface Observer{
public void update(String albumName,Date releaseDate,String state);
}
class Fan implements Observer{
public void update(String albumName,Date releaseDate,String state){...}
...
}
class Reporter implements Observer{
public void update(String albumName,Date releaseDate,String state){...}
...
}
class Developer implements Observer{
public void update(String albumName,Date releaseDate,String state){...}
...
}
인터페이스 Observer를 상속받아 update함수를 구현하도록 설계했다. Observer인터페이스를 구현한 클래스들은 옵저버가 되며 가각의 클래스들에 영향을 주지 않고 수정 삭제가 가능하다.
이제 Subject와 Observer를 이용해서 Observer가 Subject의 내용을 제공 받을 수 있도록 Subject와 Object를 완성해 보자.
Subject 클래스
class PostMalone implements SingerSubject{
private String albumName;
private Date releaseDate;
private String state;
private List<Observer> observers;
public PostMalone (){
observers = new ArrayList<Observer>();
}
// 옵저버 추가
public void addObserver(Observer o){
observers.add(o);
}
// 옵저버 삭제
public void removeObserver(Observer o){
int i = observer.indexOf(o);
if(i>=0){
observer.remove(i);
}
}
// 옵저버들에게 변경 내용 전송
public void notifyObserver(){
for(Observer o : observers){
o.update(albumName,releaseDate,state);
}
}
public void setAlbum(String albumName,Date releaseDate,String state){
this.albumName = albumName;
this.releaseDate = releaseDate;
this.state = state;
notifyObserver(); // 변경 내용을 옵저버에게 전달하기 위함
}
}
Observer클래스
// 편의상 Fan만 구현
class Fan implements Observer{
private String albumName;
private Date releaseDate;
private String state;
private SingerSubject singer;
// SingerSubject에 addObserver를 호출하여 옵저버 등록
public Fan(SingerSubject singer){
this.singer = singer;
singer.addObserver(this);
}
public void update(String albumName,Date releaseDate,String state){
this.albumName = albumName;
this.releaseDate = releaseDate;
this.state = state;
work();
}
// Fan객체가 업데이트 받은 정보를 가지고 할 작업
public void work(){
...
}
}
이런 식으로 디자인한다면 PostMalone에 setAlbum() 함수가 새로운 정보를 등록하면 등록한 정보를 가각의 옵저버들에게 전달해 주게 된다.
자바 내장 옵저버 패턴
옵저버 패턴의 경우 자바에서 이미 구현해 둔 옵저버 패턴을 사용할 수 있다. 바로 java.util 패키지에 있는 Observer 인터페이스와 Observable 클래스이다. 위 예제와 다른 점은 위 예제의 Subject 인터페이스와 다른 점은 Observable은 클래스라는 거다.
Observable클래스에는 notifyObservers(), notifyObservers(args)로 notify함수가 두 가지가 있으며 setChanged()라는 함수가 있다. 이는 pull 방식의 옵저버 패턴을 구현하기 위함이다.
pull 방식의 옵저버 패턴
pull 방식은 기존 예제와 다르게 옵저버가 주제 객체에서 필요한 데이터를 가져가도록 구현한다.
먼저 변경 사항이 생기면 setChanged 함수를 통해서 변경내용이 생겼다고 변경 플래그를 변경한다.
그럼 notify함수에서 변경 플래그를 통해서 변경사항이 있을 경우 옵저버의 update를 호출한다.
example
boolean changed = false;
setChanged(){
changed = true;
}
notifyObserver(Object arg){
if(changed){
for(Observer o : observers){
o.update(this,arg);
}
changed = false;
}
}
notifyObserver(){
if(changed){
for(Observer o : observers){
o.update(this);
}
changed = false;
}
}
위와 같은 형태가 pull 방식이다. 먼저 setChanged를 호출해 update를 호출할 수 있도록 하고 notifyObserver함수를 실행한다. Object arg가 전달한 데이터이며 arg가 없는 notifyObserver함수는 옵저버가 직접 Subject객체에서 필요한 데이터를 직접 pull 해가는 방식이다.