본문 바로가기

Dev.../패턴자료

The Observer Pattern

The Observer Pattern

    복잡한 윈도우 세계에서 우리는 종종 데이터를 하나 이상의 폼으로 동시에 보여주고 싶고 그 데이타에서 어떤 변화라도 반사하여 보여주고 싶을 때가 있다. 예를 들어 주식 가격의 변화를 그래프와 테이블이나 리스트로 표현할 지도 모른다. 매 시간 마다 가격은 변하고 우리의 어떤 조작 없이 동시에 변화를 두 가지 방법으로 표현하기를 기대하게 될 것이다.

    우리는 우리가 행위를 볼 수 있는 Excel과 같은 윈도우 어플리케이션들이 있기 때문에  행위의 정렬을 기대한다. 이런 방식으로 작동하는 프로그램에 대해서는 Observer 디자인 패턴이 유용하게 사용될 수 있다.

    Observer 패턴은 데이터를 포함하는 객체는 데이터를 보여주는 객체로부터 분리된다는 것을 가정하고 이 보여주는 객체들은 데이터의 변화를 관찰한다(observe). 아래 그림은 설명을 위한 간단한 그림이다.

       

    우리가 Observer 패턴을 구현할 때, 우리는 일반적으로 주어(Subject)로서 데이터를 참조하고 Observers로 각각의 디스플레이를 참조한다. 각각의 observer들은 Subject에서 public 메소드를 호출하여 데이터에 그것의 관심을 등록한다. 그 다음 각 observer는 Subject가 데이터가 변경될 때 호출하기 위한 인터페이스로 알려져 있다. 우리는 이러한 인터페이스를 아래처럼 정의할 수 있다.
abstract interface Observer {	//notify the Observers that a change has taken place	public void sendNotify (String s);}abstract interface Subject {	//tell the Subject you are interested in changes	public void registerInterested (Observer obs);}
이런 추상 인터페이스들을 정의하여 얻는 이점은 각각의 클래스 객체가 이 인터페이스들을 구현하기를 원하는 그 객체들의 어떤 정렬이건 작성할 수 있고, 이 객체들을 Subject 타입으로 선언할 수 있고 Observer가 어떤 일을 하는가는 문제가 되지 않는다.

Watching Colors Change

    이 강력한 개념을 어떻게 사용할 수 있는가를 설명할 수 있는 간단한 프로그램을 작성해 보자. 우리의 프로그램은 아래의 그림처럼 Red, Green 그리고 Blue라 명명된 3개의 라디오 버튼을 포함하는 프레임을 보여준다.

       

    이 메인 윈도우는 Subject 나 데이터 저장 객체이다. 우리는 이 윈도우를 JFC 클래스들을 이용하여 다음 코드처럼 작성하였다:
public class Watch2L extends JFrame 	implements ActionListener, ItemListener, Subject {		JButton close;	JRadioButton red, green, blue;	Vector observers;		//------------------------		public Watch2L() {		super("Change 2 other frames");				//list of ovserving frames		observers = new Vector();				//add panel to cotent pane		JPanel p = new JPanel(true);		p.setLayout(new BorderLayout());		getContentPane().add("Center",p);				//vertical box layout		Box box = new Box(BoxLayout.Y_AXIS);		p.add("Center", box);				//add 3 radio buttons		box.add(red = new JRadioButton("Red"));		box.add(green = new JRadioButton("Green"));		box.add(blue = new JRadioButton("Blue"));				//listen for clicks on radio buttons		blue.addItemListener(this);		red.addItemListener(this);		green.addItemLitener(this);				//make all part of same button group		ButtonGroup bgr = new ButtonGroup();		bgr.add(red);		bgr.add(green);		bgr.add(blue);
    우리의 메인 클래스는 Subject 인터페이스를 구현한 클래스라는 걸 주목하자. 그 것은 이 클래스에서 관심있는 데이터를 등록하기 위한 public 메소드를 제공해야만 한다는 것을 의미한다. 이 메소드는 registerInterest 메소이고 단지 하나의 Vector에 Observer 객체들을 추가한다:
public void registerInterest (Observer obs) {	//adds obserers to list in Vector	observers.addElement(obs);}
    이제 우리가 두 개의 observers를 생성해서 하나는 색을 나타내고 다른 하나는 현재의 색을 리스트 박스에 추가하자.
//-------create observers-----------ColorFrame cframe = new ColorFrame(this);ListFrame lframe = new ListFrame(this);
ColorFrame 윈도우를 생성할 때 우리의 관심있는 데이터를 메인 프로그램에 등록을 한다:
class ColorFrame extends JFrame implements Observers {	Color color;	String color_name = "black";	JPanel p = new JPanel(true);		//---------------------------	public ColorFrame(Subject s) {		super("Color");		//set frame caption		getContentPane().add("Center",p);		s.registerInterest(this);	//register with Subject		setBounds(100, 100, 100, 100);		setVisible(true);	}		//-----------------------------	public void sendNotify(String s) {		//Observer is nofified of change here		color_name = s;	//save color name		//set background to that color		if(s.toUpperCase().equals("RED"))			color = color.red;		if(s.toUpperCase().equals("GREEN"))			color = color.green;		if(s.toUpperCase().equals("BLUE"))			color = color.blue;							setBackground(color);	}		//-----------------------------	public void paint(Graphics g) {		g.drawString(Color_name, 20, 50);	}
그 동안 메인 프로그램에서는 라디오 버튼 중의 하나를 누를 때마다 Observers 벡터에 있는 객체들을 통하여 간단하게 실행되어 이러한 변화들에 대한 관심을 등록하는 각각의 Observer 의 sendNotify 메소드를 호출한다:
public void itemStateChanged(ItemEvent e) {	//responds to radio button clicks	//ifthe button is selected	if(e.getStateChange() == ItemEvent.SELECTED)		notifyObservers((JRadioButton)e.getSource());	}	//----------------------------private void notifyObservers(JRadioButton rad) {	//send text of selected button to all observsers	String color = rad.getText();	for (int i=0; i < observers.size(); i++) {		((Observer)(observsers.elementAt(i))).sendNotify(color);	}}			
    ColorFrame observer의 경우에서, sendNotify 메소드는 배경색과 프레임 안의 패널에 텍스트를 바꾼다. 그러나 ListFrame observer 경우에는 단지 리스트 박스에 새로운 색의 이름을 추가한다. 아래 그림의 최종적인 프로그램이다 :

       
 

The Message to the Media

Subject가 observers에게 보내는 통지(notification)의 종류는 무인인가? 이  제한적인 예에서 통지 메시지는 색 자체를 표현한 문자열이다. 라디오 버튼을 클릭할 때 그 버튼에 대한 캡션을 얻고 observers 에게 그 캡션을 보낸다. 물론 이것은 모든 observer 들이 문자로 표현된 핸들링을 할 수 있다는 것이다. 보다 실제적인 상황에서 이 방법은 항상 사용할 수 있는 것은 아니다. 특히 observer들이 다른 데이터 객체들을 관찰하는데 사용되어 질 수 있다면 이 방법은 사용되지 않을 수 있다. 여기서 우리는 두 가지 간단한 데이터 변환을 시작한다:
  1. 우리는 라디오 버튼에서 라벨을 얻어 observers에 보낸다. 그리고
     
  2. ColorFrame observer 에서 실제적인 색으로 라벨을 변환한다.

    좀더 복잡한 시스템에서 우리는 요구되는 특성을 관찰할 수 있다. 각각의 observer가 메시지를 정확한 데이터 타입으로 변환하는 것보다 이런 변환을 수행할 중간의 Adapter 클래스를 사용할 수 있다.

    observer들이 처리해야 만 할지도 모르는 또 다른 문제는 중심적인 subject 클래스의 데이터가 여러 가지 방법으로 바뀔 수 있는 경우이다. 우리는 데이터의 목록에서 문제되는 것을 지우고 값을 편집하거나 데이터가 보여줄 수 있는 범위를 바꿀 수 있다. 이런 경우에 우리는 observer들에게 다른 변경 메시지를 보내거나 하나의 메시지를 보내고 발생되는 변경의 정렬을 요청하는 observer를 가지고 있을 필요가 있다.

The MVC Architecture of an Observer

JList, JTable 과 JTree 객체는 모두 데이터 모델의 observer들로 작동한다. 사실 JComponent로부터 파생 받은 모든 비쥬얼 컴포넌트들은데이터와 시각적인 표현 사이의 작업이 이와 같은 분배를 가지고 있다. JFC에서는 데이터는 모델로 표현하고 뷰는 비쥬얼 컴포넌트로 표현하는 모델-뷰-컨트롤러(Model-View-Controller : MVC) 아키텍쳐를 사용하였다. 그리고 컨트롤러는 모델과 뷰 객체들 사이의 통신이며 분할된 클래스이거나 모델이나 뷰에서 상속 받을 수도 있다.

Consequences of the Observer Pattern

    Observer들은 Subject들에게 추상적인 커플링을 조장한다. 어떤 Subject는 Subject의 observer들에 대해 자세히 알지 못한다. 그러나 이것은 데이터를 변경시키는 것이 증가할 때 Observer들을 연속적이거나 반복적으로 업데이트를 해야 하는 단점을 가질 수 있다. 업데이트 비용이 높다면 Observer들이 너무 자주 통보 받지 않도록 변경을 관리하는 어떤 정렬을 도입할 필요가 있다.

'Dev... > 패턴자료' 카테고리의 다른 글

The Mediator Pattern  (0) 2005.03.01
The Chain of Responsibility Pattern  (0) 2005.03.01
Behavioral Patterns  (0) 2005.02.28
Summary of Structural Patterns  (0) 2005.02.28
The Decorator Pattern  (0) 2005.02.28