책임 떠넘기기 패턴
어떤 처리 요구가 들어왔을때, 그것을 처리 할 수 있는 인스턴스가 나타날때까지
그 처리를 넘기는 패턴이다.
예제프로그램은 임의의 숫자를 가진 트러블을 발생시키고, 트러블을 해결하는 클래스들을 종류별로
만들어 놓은 후 Chain of Responsibility패턴을 구현한 것이다.
1.Trouble - 발생한 트러블을 나타내는 클래스. 트러블 번호를 갖는다.
2.Support - 트러블을 해결하는 추상 클래스
3.NoSupport - 트러블을 해결하는 클래스 (항상 처리하지 않음.)
4.LimitSupport - 트러블을 해결하는 클래스
....
Trouble Class
public class Trouble {
private int number; // 트러블 번호
public Trouble(int number) { // 트러블의 생성
this.number = number;
}
public int getNumber() { // 트러블 번호를 얻는다.
return number;
}
public String toString() { // 트러블의 문자열 표현
return "[Trouble " + number + "]";
}
}
트러블을 나타내면 트러블 번호를 갖는다. 나중에 처리하는 클래스들은 이 번호를 가지고 자신이
처리 할 수 있는지의 여부를 결정한다.
Support Class
public abstract class Support {
private String name; // 트러블 해결자의 이름
private Support next; // 떠넘기는 곳
public Support(String name) { // 트러블 해결자의 생성
this.name = name;
}
public Support setNext(Support next) { // 떠넘길 곳을 설정
this.next = next;
return next;
}
public final void support(Trouble trouble) { // 트러블 해결 순서
if (resolve(trouble)) {
done(trouble);
} else if (next != null) {
next.support(trouble);
} else {
fail(trouble);
}
}
public String toString() { // 문자열 표현
return "[" + name + "]";
}
protected abstract boolean resolve(Trouble trouble); // 해결용 메소드
protected void done(Trouble trouble) { // 해결
System.out.println(trouble + " is resolved by " + this + ".");
}
protected void fail(Trouble trouble) { // 미해결
System.out.println(trouble + " cannot be resolved.");
}
}
next필드는 Support형으로 다음 문제를 해결할 클래스의 인스턴스를 가지고 있으며, 이는 setNext
메소드를 통해 설정 할 수 있다. resolve메소드는 하위 클래스에서 구현할 것을 상정한 추상 메소드입니다. 반환값이 true이면 해결한거고 아니면 해결 못 한 것..
NoSupport Class
public class NoSupport extends Support {
public NoSupport(String name) {
super(name);
}
protected boolean resolve(Trouble trouble) { // 해결용 메소드
return false; // 자신은 아무 처리도 하지 않는다.
}
}
LimitSupprot Class
public class LimitSupport extends Support {
private int limit; // 이 번호 미만이면 해결 할수 있다.
public LimitSupport(String name, int limit) { // 생성자
super(name);
this.limit = limit;
}
protected boolean resolve(Trouble trouble) { // 해결용 메소드
if (trouble.getNumber() < limit) {
return true;
} else {
return false;
}
}
}
Support클래스를 상속하고 있는 실제 처리 클래스들은 다 이런식으로 구성되어있다.
Main..
public class Main {
public static void main(String[] args) {
Support alice = new NoSupport("Alice");
Support bob = new LimitSupport("Bob", 100);
Support charlie = new SpecialSupport("Charlie", 429);
Support diana = new LimitSupport("Diana", 200);
Support elmo = new OddSupport("Elmo");
Support fred = new LimitSupport("Fred", 300);
// 연쇄의 형성
alice.setNext(bob).setNext(charlie).setNext(diana).setNext(elmo).setNext(fred);
// 다양한 트러블 발생
for (int i = 0; i < 500; i += 33) {
alice.support(new Trouble(i));
}
}
}
해결 인스턴스별로 각각 6개를 생성하고 연쇄를 형성한다. 이것은
alice인스턴스의 next필드에는 bob이
bob인스턴스의 next필드에는 charlie가..
이런식으로 들어가 있게 되는 것이고, 이것은 Support클래스에서
public final void support(Trouble trouble) { // 트러블 해결 순서
if (resolve(trouble)) {
done(trouble);
} else if (next != null) {
next.support(trouble);
} else {
fail(trouble);
}
여기서 사용된다. 예를 들어 Main클래스에서
로 최초 호출을 하고 있는데, 이는 alice의 resolve가 호출되고 alice는 무조건 false를 return하므로 , else문에 의해서 next.support(trouble)이 실행된다.
alice의 next는 bob이므로 bob.support와 같은 의미이고 이는 bob의 resolve를 호출하게 되는
이런 방식이다. (재귀적 호출)
즉, handler 클래스를 두고 이 클래스가 자기 자신을 호출하여 자기 필드에 있는 실제 처리 인스턴스들을 사용해 처리를 넘기는 것이다.
이패턴이 사용되지 않으면 요구를 하는 사람이 누가 처리할지 까지 알고 있어야 한다.