본문 바로가기

Dev.../패턴자료

The Mediator Pattern

The Mediator Pattern

    어떤 프로그램이 여러 개의  클래스로 만들어져 있을 때 로직과 계산은 이러한 클래스로부터 논리적으로 나뉘어 진다. 그러나 이러한 고립적인 클래스들이 증가함에 따라 이러한 클래스들의 통신 문제가 보다 복잡해졌다. 다른 클래스의 메소드들에 대하여 아는 것이 필요한 클래스가 더 많아지고 클래스들의 구조는 더 얽혀 복잡해 진다. 이것이 프로그램을 읽는 것과 유지를 어렵게 한다. 게다가 프로그램을 변경하는 것이 어려운데 왜냐면 어떤 변화가 여러 개의 다른 클래스들에서 코드에 영향을 줄 지도 모르기 때문이다. Mediator 패턴은 이러한 클래스들 사이의 느슨한 커플링을 진행하여 이러한 문제를 해결할 수 있는 방법을 소개한다. Mediator는 다른 클래스들의 메소드들에 대한 정보를 설명한 오직 하나의 클래스의 이용하여 이 문제를 해결할 수 있다. 클래스들은 변경되었을 때 mediator에게 정보를 준고 Mediator는 정보를 필요로 하는 다른 클래스들에게 정보를 전달해 준다.

An Example System

    여러 개의 버튼과 두 개의 리스트 박스와 입력할 수 있는 텍스트 필드를 갖는 프로그램을 생각해 보자:

       

    프로그램이 시작되었을 때는 Copy 와 Clear 버튼은 비 활성 상태이다.
  1. 좌측의 리스트 박스에서 이름들 중의 하나를 선택했을 때, 편집을 위한 텍스트 필드에 복사가 되고 Copy 버튼은 활성화 된다.
     
  2. Copy 버튼을 눌렀을 때, 그 텍스트는 우측의 리스트 박스에 추가되고 Clear 버튼이 활성화 된다.

        
     
  3. Clear 버튼을 눌렀다면 우측의 리스트 박스와 텍스트 필드는 지워지고 리스트 박스는 선택할 수 없고 두 개의 버튼은 다시 비 활성 상태가 된다.
    이와 같은 사용자 인터페이스는 공통적으로 사람이나 생산물의 목록의 선택에서 사용되어진다. 게다가 이렇게 만든 것보다 삽입, 삭제  와 취소와 같은 기능이 추가되어 더 복잡해 질 수 있다.

Interactions between Controls

시각적인 컨트롤들 사이에 상호작용은 이러한 간단한 예 조차 꾀 복잡하다. 각각의 시각적인 객체는 두 개나 더 이상의 객체들에 관해서 알 필요가 있어 아래 그림처럼 관계들이 얽혀 있을 수 있다.

   

    Mediator 패턴은 이 시스템에서 다른 클래스들을 인식할 수 있는 오직 하나의 클래스 두어 이 시스템을 단순화한다. Mediator 와 통신 하는 각각의 컨트롤들을 Colleague 라 부른다. 각각의 Colleague 는 사용자 이벤트를 받았을 때 Mediator 에게 통지하고 Mediator 는 이벤트가 통지 될 수 있는 다른 클래스들을 결정한다. 아래 그림은 상호 작용 구조를 단순화한 것이다.



    Mediator의 장점은 명확하다 -- 그것은 다른 클래스들에 대해 아는 오직 하나의 클래스를 갖고 그래서 다른 클래스들 중의 하나가 바뀌거나 다른 인터페이스 컨트롤 클래스가 추가된다면 그 클래스는 변경할 필요가 있다.

Sample Code

이 프로그램을 자세하게 고려하고 각각의 컨트롤이 어떻게 구축할 지를 결정해 보자. Mediator 클래스를 사용하는 프로그램 작성에서 주요 차이점은 각각의 클래스들은 Mediator의 존재를 알 필요가 있다. Mediator의 인스턴스를 만들어 시작하고 각 클래스의 생성자에 Mediator의 인스턴스를 전달한다. 
Mediator med = new Mediator();kidList = new KidList(med);tx = new KTextField(med);Move = new MoveButton(this, med);Clear = new ClearButton(thism med);med.init();
    우리는 각 컨트롤에 대해 새로운 클래스들을 만들었기 때문에 각 클래스 내에서 mediator 연산자를 조정할 수 있다.

    두 개의 버튼은 Command 패턴을 사용했고 초기화 되는 동안 Mediator에 등록하였다. CopyButton에 대한 코드는 아래와 같다 :
public class MoveButton extends JButton implements Command {	Mediator med;            //copy of the Mediator		public MoveButton(ActionListener fr, Mediator md) {		super("Copy");         //create the button		addActionListener(fr); //add its listener		med = md;              //copy in the Mediator instance		med.registerMove(this); //register with the Mediator   	}		public void Execute() {		//execute the copy		med.Move();	}}
Clear 버튼은 거의 비슷하다.
   
    아이의 이름 목록은 최근에 예에서 사용한 것에 기본을 두고 있지만 리스트의 데이터 로딩과 Mediator에 등록을 하기 위해 확장을 하였다. 추가적으로 우리는 ListSelectionListener를 구현하였고 어떤 리스트 항목을 누르건 Mediator에 정보를 줄 수 있도록 하였다.
public class KidList extends JawtList implements ListSelectionListener {	KidData kdata;	Mediator med;		public KidList(Mediator md) {		super(20);		kdata = new KidData ("50free.txt");		fillKidList();		med = md;		med.registerKidList(this);		addListSelectionListener(this);	}	    	//----------------------------------	public void valueChanged(ListSelectionEvent ls) {		JList obj = (JList)ls.getSource();		if (obj.getSelectedIndex() >= 0)		med.select();	}		//----------------------------------	private void fillKidList() {		Enumeration ekid = kdata.elements();		while (ekid.hasMoreElements()) {			Kid k =(Kid)ekid.nextElement();			add(k.getFrname()+" "+k.getLname());		}	}}
    텍스트 필드는 그 자체를 mediator에 등록을 하기 때문에 훨씬 간단하다.
public class KTextField extends JTextField {	Mediator med;		public KTextField(Mediator md) {		super(10);		med = md;		med.registerText(this);	}}
    이러한 클래스들 모두의 공통점은 각각의 것들이 Mediator에 대하여 알고 Mediator에게 그것의 존재를 알려줘 Mediator는 적절한 시기에 명령들을 보내 줄 수 있다.

    Mediator 그 자체는 아주 간단하다. 그것은 Copy, Clear 와 Select 메소드를 지원하고 각 컨트롤들을 위해 메소드들을 등록해 둔다:
public class Mediator {	private ClearButton clearButton;	private MoveButton moveButton;	private KTextField ktext;	private KidList klist;	private PickedKidsList picked;		public Mediator() {	}	//------------------------------------	public void Move() {		picked.add(ktext.getText());		clearButton.setEnabled(true);	}		//------------------------------------	public void init() {		Clear();	}		//------------------------------------	public void Clear() {		ktext.setText("");		moveButton.setEnabled(false);		clearButton.setEnabled(false);		picked.clear();		klist.clearSelection();		System.out.println("cleared");	}		//------------------------------------	public void select() {		String s = (String)klist.getSelectedValue();		ktext.setText(s);		moveButton.setEnabled(true);		System.out.println("selected");	}		//------------------------------------	public void registerClear(ClearButton cb) {		clearButton = cb;	}		//------------------------------------	public void registerMove(MoveButton mv) {		moveButton = mv;	}		//------------------------------------	public void registerText(KTextField tx) {		ktext = tx;	}		//------------------------------------	public void registerPicked(PickedKidsList pl) {		picked = pl;	}		//------------------------------------	public void registerKidList(KidList kl) {		klist = kl;	}}
시스템의 초기화

    Mediator를 가장 정교하게 하는 연산자 중의 하나는 요구되는 상황에 대한 모든 컨트롤의 초기화이다. 언제 우리가 프로그램을 시작할 때 각 컨트롤들은 기본 상태에서 알려져 있어야 한다. 왜냐면 이러한 상태들은 프로그램이 진화함에 따라 변경될 수 있기 때문에 우리는 간단히 요구되는 모든 상태에 대해 세팅해 주는 init 메소드를 Mediator에 만들었다. 이 경우에 상태는 Clear 버튼에 의해 이루어지는 것과 같고 우리는 간단하게 이 메소드를 호출할 수 있다 :

public void init() {
    Clear();
}

Mediators and Command Objects

이 프로그램에서 두 개의 버튼은 command 객체들이고 이러한 버튼들을 초기화 할 때 ActionListener로 사용자 인터페이스 프레임에 등록을 하였다. 우리가 앞에서 보았던 것처럼 이것은 간단하게 버튼 클릭 이벤트를 처리한다.

public void actionPerformed(ActionEvent e) {
    Command comd = (Command)e.getSource();
    comd.Execute();
}
대안적으로 우리는 자신의 리스너를 가진 상속 받은 클래스를 등록하여 Mediator에 직접 결과를 전달할 수도 있다.

    그러나 두 가지 모두 Command 패턴 단원에서 보았던 문제들에 대한 해결책 중의 하나이다.

Consequences of the Mediator Pattern

  1. Mediator는 프로그램에서 객체들 사이의 가능한 느슨한 커플링을 만든다. 또 그 밖에 것들이 여러 객체들 사이에서 방해할 수 있는 행동을 줄여줄 수 있다.
     
  2. 간단하게 바꾸거나 Mediator의 하위 클래스로 프로그램의 행위를 바꿀 수 있다.
     
  3. Mediator 접근은 새로운 Colleague를 프로그램의 다른 부분을 변경하는 것 없이 추가할 수 있다.
     
  4. Mediator는 사용자 인터페이스에서 나머지 객체들이나 메소들에 대해 너무 많이 아는 Command 객체의 문제를 해결한다.
     
  5. Mediator는 바꾸기 힘들고 유지하기 힘든 복잡한 덩어리가 될 수 있다. 종종 Mediator에 주어진 책임들을 수정하여 이 상황을 개선할 수 있다. 각 객체는 자신의 일을 수행하고 Mediator는 단지 객체들 사이의 상호작용만 관리한다.
     
  6. 각각의 Mediator는 각각의 Colleague들을 호출할 수있는 메소드를 가지고 있고  각각의 Colleague가 이용하는 메소드들이 무엇인지를 알 수 있도록 작성된 클래스이다. 이것이 다른 프로젝트에서 Mediator 코드가 재사용되는 것을 어렵게 한다. 반면에 Mediator들은 아주 간단한고 이 코드를 작성하는 것이 어떤 방법보다 객체들의 복잡한 상호작용을 관리하는 것 보다 훨씬 더 쉽다.

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

The Strategy Pattern  (0) 2005.03.01
The Template Pattern  (0) 2005.03.01
The Chain of Responsibility Pattern  (0) 2005.03.01
The Observer Pattern  (0) 2005.03.01
Behavioral Patterns  (0) 2005.02.28