소프트웨어 개발/Design Pattern

⑫ 디자인 패턴(Design Pattern) - Chain of responsibility

늘근이 2015. 9. 26. 14:06


Chain of responsibility라는 이름에 답이 있다.

각자의 클래스는 어떤 일을 처리할수 있는데, 가끔씩은 내가 처리할수 없는 일도 있다. 이러한 경우 내 선에서 굳이 처리하지 않아도 되고 판단하에 다음 클래스를 태워서 판단하게 할수 있다.

만약, 은행대기열에서 짝수 대기열만 처리하는 은행원도 있으며 홀수만 처리하는 은행원도 있다고 치자.  대기열을 받기는 받는데, 내가 처리하여야 할 상황이 아니면 다른놈으로 넘겨버려야 한다.

이는 마치 회사 상황과도 같는데, 일단 특정 프로세스에 문의사항이 생긴 직원운 지가 찾아볼 생각은 안하고 대충 담당자일것 같은 사람에게 메일을 보내고 바로 처리해주거나 은근히 다른 담당자를 찾아서 포워딩해주기를 바란다. 이제 토스가 일어나는 순간이다.

문의를 받은 직원은 이게 자기가 처리해야할 상황인지, 아닌지 판단이 서지 않지만 애매하니 일단 확실한 사람이라고 여겨지는 사람에게 토스해버리고 토스를 당한사람은 이번에 처리해주면 또 시킬까봐 이걸 또 웬만한 사람에게 토스해버린다.

책임과 귀찮은 일이 계속 메일 참조가 걸리며 포워딩(setNext메서드) 이 되는데 일이 애매한건 아무것도 모르는 막내가 받아서 처리하게 되겠다..


먼저 이슈가 하나 있다. 짝수 홀수란 개념만 가지고 있다.

public class Issue {
	private int number;

	public Issue(int number) {
		this.number = number;
	}

	public int getNumber() {
		return number;
	}	
}


이를 처리하기 위한 Helper 클래스도 존재한다. (Handler)

public abstract class Helper {
	
	private String name;
	private Helper next;

	public Helper(String name) {
		this.name = name;
	}

	public Helper setNext(Helper next) {
		this.next = next;
		return next;
	}

	public final void help(Issue issue) {
		if      (solve(issue)) System.out.println(name + " 으로부터 이슈가 해결되었습니다.");
        else if (next != null)     next.help(issue);
        else     System.out.println("해결되지 않았습니다");
	}

	protected abstract boolean solve(Issue issue);

}

좀 복잡하기는 한데, 크게 두가지 기능이 있다.

setNext()는 다음놈에게 토스해 버리는 로직이 구현되어있다. next변수는 Helper 타입이다. 즉, setNext는 연쇄적으로 쓰일수 있으며 계속해서 다음 놈에게 토스해버릴수 있는 구조를 만들수 있다.

help()메서드에는, 일단 자기가 처리해보고 결과가 참이면 그대로 끝내며 결과가 거짓이면 (처리하지 못하면) 다음 놈의 help()메서드를 부른다. 마지막으로 최후의 보루에서도 처리하지 못하면 해결되지 않았다는 메시지를 띄워준다.

이 Helper 클래스는 위의 예제에는 바로 쓰지는 못하는 추상클래스로 되어있는데, solve()메서드가 추상메서드이기 때문이다. 어떻게 문제를 풀지는 Helper클래스를 상속받은 ConcreteHelper 클래스에서 처리하도록 해놓으면 된다.


ConcreteHelper클래스는 다음과 같다. 짝수를 처리하는 EvenHelper, 그리고 홀수를 처리하는 OddHelper, 아무것도 처리하지않고 업무토스만 하는 NoHelper가 있다.


public class EvenHelper extends Helper{
	
	public EvenHelper(String name) {
		super(name);
	}
	
	protected boolean solve(Issue issue) {
		if(issue.getNumber() % 2 == 0) return true;
		else						   return false;
	}
}


public class OddHelper extends Helper{
	
	public OddHelper(String name) {
		super(name);
	}
	
	protected boolean solve(Issue issue) {
		if(issue.getNumber() % 2 == 1) return true;
		else						   return false;
	}
}


public class NoHelper extends Helper{
	
	public NoHelper(String name) {
		super(name);
	}
	
	protected boolean solve(Issue issue) {
		return false;
	}
}


이제 실제로 다음과 같이 이용해볼수있다.

public class Main {
	public static void main(String[] args) {
		
		Helper noHelper   = new NoHelper("NO HELPER");
		Helper oddHelper  = new OddHelper("ODD HELPER");
		Helper evenHelper = new EvenHelper("EVEN HELPER");
		
		noHelper.setNext(oddHelper).setNext(evenHelper);
		
		for(int i = 0; i < 100 ; i++) {
			System.out.println(i + "를 해결합니다");
			noHelper.help(new Issue(i));
		}
		
		
		
	}
}

각자의 인스턴스를 만들고 setNext()메서드로 계속 다음 helper를 설정해주고 있다.


결과는 다음과 같다.

0
EVEN HELPER 으로부터 이슈가 해결되었습니다.
1
ODD HELPER 으로부터 이슈가 해결되었습니다.
2
EVEN HELPER 으로부터 이슈가 해결되었습니다.
3
ODD HELPER 으로부터 이슈가 해결되었습니다.
4
EVEN HELPER 으로부터 이슈가 해결되었습니다.
5
ODD HELPER 으로부터 이슈가 해결되었습니다.

.....

94
EVEN HELPER 으로부터 이슈가 해결되었습니다.
95
ODD HELPER 으로부터 이슈가 해결되었습니다.
96
EVEN HELPER 으로부터 이슈가 해결되었습니다.
97
ODD HELPER 으로부터 이슈가 해결되었습니다.
98
EVEN HELPER 으로부터 이슈가 해결되었습니다.
99
ODD HELPER 으로부터 이슈가 해결되었습니다.

 

이 디자인 패턴으로 되어있는 자바의 API는 javax.servlet.Filter # doFilter() 메서드와 java.util.logging.Logger # log 가 있다. 필터는 자신이 필요한 부분을 필터를 걸고, 다음 필터를 적용하는 과정을 거치게 된다.