본문 바로가기

Dev.../패턴자료

The Chain of Responsibility Pattern

The Chain of Responsibility Pattern

    Chain of Responsibility 패턴은 다른 클래스들의 능력에 관해 아는 것 없이 어떤 요청을 조정하려 하는 클래스들을 허용하는 패턴이다.  그것은 이러한 클래스들 사이에 느슨한 커플링을 제공한다; 오직 공통적인 링크는 그것들 사이에서 전달되는 요청이다. 그 요청은 클래스들 중의 하나가 그것을 다룰 수 있을 때 까지 전달된다.

    그러한 체인 패턴 중의 하나의 예는 도움말 시스템이다.
도움말에 대한 영역을 선택했을 때, 컨트롤들이 그 체인의 ID 나 이름을 진행시킨다. 우리가 "New" 버튼을 선택했을 때를 가장해 보자. 만약 첫 번째 모듈이 New 버튼을 조정할 수 있다면 그것을 도움말 메시지를 표시한다. 그렇지 않다면 다음 모듈에 대한 요청을 진행시킨다. 결국, 메시지는 일반적으로 어떻게 버튼들이 작동하는가를 나타낼 수 모든 버튼들의 클래스로 진행된다. 거기에 어떤 일반적인 버튼 도움말이 없다면, 그 메시지는 일반적으로 시스템이 어떻게 동작하는지를 말해줄 수 있는 일반적인 도움말 모듈로 진행된다. 그것조차 없다면 그 메시지는 사라지게 되고 어떤 정보도 나타나지 않는다. 아래의 그림은 이러한 과정을 설명한 그림이다.

       
    이 예에서 우리가 관찰할 수 있는 두 가지 의미는 첫째는 체인은 가장 특수한 것에서 가장 일반적인 것으로 구조화 된다는 것이고, 둘째는 모든 경우에서 요청은 책임을 만들어 내는 담보가 없다는 것이다.

Applicability

우리는 다음과 같은 상황에서 Chain of Responsibility를 사용한다.
  • 요청에 대해 조정할 수 있는 하나 이상의 핸들러를 가지고 있고, 사용하는 핸들러를 알아낼 방법이 없을 때. 핸들러는 반드시 체인에 의하여 자동적으로 결정되어야 한다.
  • 명확하게 결정하는 것 없이 여러 객체들 중 하나의 객체에 대한 요청을 이끌러 내려 할 때
  • 요청들을 조정할 수 있는 동적인 객체들의 집합을 수정할 수 있기를 원할 때

Sample Code

    입력된 요청에 따라 결과들을 보여 줄 수 있는 간단한 시스템을 고려해 보자. 이러한 요청들이 있을 수 있다.
  • Image filenames
  • General filenames
  • Colors
  • Other commands
    세 가지 경우에 대해서는 구체적인 요청의 결과를 나타낼 수 있고, 마지막 경우에는 요청한 텍스트 그 자체만 나타낼 수 있다.

       

    위의 예제에서, "ribbons"이라 입력을 하면 "ribbon.jpg" 가 나타나는 걸 볼 수 있다. 그리고 나서, "Sender"라고 입력을 하면, 리스트 박스에서 "Sender.class" 파일일 하이라이트 되는 것을 볼 수 있다. 그 다음, "blue" 라고 입력을 하면 아래의 중앙 패널에 파란색이 칠해지는 걸 볼 수 있다. 마지막으로 파일이름이나 색이 아닌 다른 것을 입력하면 우측이 리스트 박스에 입력한 내용이 추가되는 걸 볼 수 있다.

       

    Responsibility of Chain 프로그램을 작성하기 위해, 먼저 추상 Chain 클래스를 작성한다.
	public interface Chain {	public abstract void addChain(Chain c);	public abstract void sendToChain(String mesg);	public Chain getChain();}
    addChain 메소드는 또 다른 클래스를 클래스들의 체인에 추가한다. getChain 메소드는 메시지가 진행될 때 현재 클래스를 반환한다. 이 두 메소드들은 체인을 동적으로 수정하는 걸 가능하게 하고 존재하는 체인의 가운데에 추가적인 클래스들을 추가한다. sendToChain 메소드는 체인안에서 다음 객체에 메시지를 보낸다.

    우리의 Imager 클래스는 JPanel로 부터 상속되고 Chain interface를 구현한다. 이 클래스는 메시지를 받아 루트 이름이 ".jpg"인 파일을 찾는다. 그런 파일이 있으면 표시를 해준다.
public class Imager extends JPanel implements Chain {	private Chain nextChain;	private Image img;	private boolean loaded;	//------------------------------------------	public void addChain(Chain c) {		nextChain = c;    //next in chain of resp	}	//------------------------------------------	public void sendToChain(String mesg) {		//if there is a JPEG file with this root name		//load it and display it.		if (findImage(mesg))			loadImage(mesg+".jpg");		else			//Otherwise, pass request along chain			nextChain.sendToChain(mesg);	}	//------------------------------------------	public Chain getChain() {		return nextChain;	}          	//------------------------------------------	public void paint(Graphics g) {		if (loaded) {			g.drawImage(img, 0, 0, this);		}	}

The List Boxes

    파일 목록과 인식되지 않는 명령들의 목록 모두 JList 박스들이다. RestList 클래스는 체인의 끝이고 어떤 명령이든 나타내어 준다.그러나 편리한 확장을 허용하기 위해 우리는 메시지를 다른 클래스들로 전달할 수 있다. 
public class RestList extends JawtList implements Chain {	private Chain nextChain = null;		public RestList() {		super(10);     //arg to JawtList		setBorder(new LineBorder(Color.black));	}		public void addChain(Chain c) {		nextChain = c;	}		public void sendToChain(String mesg) {		add(mesg);        //this is the end of the chain		repaint();		if(nextChain != null)		nextChain.sendToChain(mesg);	}		public Chain getChain() {		return nextChain;	}}        
    FileList 클래스는 꾀나 유사하고, addChain 과 getChain 메소드의 반복을 피하기 위하여  RestList 클래스로부터 파생될 수 있다. 유일한 차이는 리스트가 초기화 될 때 현재 디렉토리에서 파일들의 목록을 읽어 들이고 요청을 받은 파일이 있는지 찾아 본다.
public class FileList extends RestList {	String files[];	private Chain nextChain;		public FileList() {		super();		setBorder(new CompoundBorder(new EmptyBorder(5,5,5,5), new LineBorder(Color.black)));		String tmp = "";		File dir = new File(System.getProperty("user.dir"));		files = dir.list();				for(int i = 0; i < files.length; i++) {			for(int j = i; j < files.length; j++) {		     		if(files[i].toLowerCase().compareTo(files[j].toLowerCase()) > 0) {				         tmp = files[i];				         files[i] = files[j];				         files[j] = tmp;		      		}		     	}		}				for(int i = 0; i < files.length; i++)			add(files[i]);	}		//---------------------------------------		public void sendToChain(String mesg) {		boolean found = false;		int i = 0; 				while ((! found) && (i < files.length)) {			XFile xfile = new XFile(files[i]);			found = xfile.matchRoot(mesg);			if (! found) i++;		}				if(found) {		   	setSelectedIndex(i);		}				else {			if(nextChain != null)		      		nextChain.sendToChain(mesg);		}	}
우리가 위에서 소개한 Xfile 클래스는 어떤 파일의 루트이름과 문자열을 비교하기 위한 matchRoot 메소드를 포함하는 File 클래스의 간단한 자식 클래스이다.

    마지막으로, 우리는 그 체인을 형식화하기 위한 생성자에서 이러한 클래스들을 연결시킨다.
//set up the chain of responsibilitysender.addChain(imager);imager.addChain(colorImage);colorImage.addChain(fileList);fileList.addChain(restList);

Consequences of the Chain of Responsibility

  1. 이 패턴의 주요 목적은 객체들 사이의 커플링을 제거하는 것이다. 하나의 객체만 어떻게 다른 객체들에게 요청을 하는지만 알면 된다.
     
  2.  이 접근 방법은 또한 객체들 사이의 분산 responsibilities 에 대해서 유연성을 줄 수 있다. 어떤 객체이든 요청들을 만족할 수 있고, 실행시 체인과 responsibilities를 변경할 수 있다.
     
  3. 장점은 요청을 조정할 수 있는 어떤 객체가 어느것이든 될 수 없다는 것이다. 그러나 체인에서 마지막 객체는 조정할 수 없는 어떤 요청을 버릴 수 있다.
     
  4. 마지막으로, 자바는 다중 상속을 제공하지 않기 때문에, 기본 체인 클래스는 추상클래스보다는 인터페이스를 필요로 한다.

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

The Template Pattern  (0) 2005.03.01
The Mediator Pattern  (0) 2005.03.01
The Observer Pattern  (0) 2005.03.01
Behavioral Patterns  (0) 2005.02.28
Summary of Structural Patterns  (0) 2005.02.28