상황에 따라 요청을 처리할 객체가 변할 수 있는데 이런 상황에 사용하는 패턴
'요청하는 쪽'과 '처리하는 쪽'의 결합을 낮춘다
: 발생한 문제
: 문제를 해결하는 추상 클래스
: 문제를 해결하는 구현 클래스
@Getter
public class Trouble {
// 트러블 번호
private int number;
public Trouble(int number) {
this.number = number;
}
@Override
public String toString() {
return "[Trouble " + number + "]";
}
}
@Setter
public abstract class Support {
// 이 트러블 해결자의 이름
private String name;
// 다음 차례로 넘기는 곳
private Support next;
public Support(String name) {
this.name = name;
}
// 문제 해결 절차
public void support(Trouble trouble) {
if (resolve(trouble)) { // 해결해보려한다
done(trouble); // 해결했다
} else if (next != null) { // 해결 못하는데 다음 해결클래스가 있으면
next.support(trouble); // 처리를 떠넘긴다
} else {
fail(trouble); // 실패
}
}
@Override
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.");
}
}
public class NoSupport extends Support {
public NoSupport(String name) {
super(name);
}
@Override
protected boolean resolve(Trouble trouble) {
return false; // 자기는 문제 처리하지 않는다는 뜻
}
}
public class LimitSupport extends Support {
private int limit;
public LimitSupport(String name, int limit) {
super(name);
this.limit = limit;
}
// limit 미만이면 해결 가능
@Override
protected boolean resolve(Trouble trouble) {
if (trouble.getNumber() < limit) {
return true;
} else {
return false;
}
}
}
public class OddSupport extends Support {
public OddSupport(String name) {
super(name);
}
// 홀수 번호라면 해결 가능
@Override
protected boolean resolve(Trouble trouble) {
if (trouble.getNumber() % 2 == 1) {
return true;
} else {
return false;
}
}
}
public class SpecialSupport extends Support {
private int number;
public SpecialSupport(String name, int number) {
super(name);
this.number = number;
}
// number와 같으면 해결 가능
@Override
protected boolean resolve(Trouble trouble) {
if (trouble.getNumber() == number) {
return true;
} else {
return false;
}
}
}
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");
alice.setNext(bob).setNext(charlie).setNext(diana).setNext(elmo);
// 다양한 문제 발생
for (int i = 0; i < 500; i += 33) {
alice.support(new Trouble(i));
}
}
}
중요하다 생각하는건 결국 어떠한 요구에 대해서 하나의 클래스가 모두 책임을 진다는건 문제라는 것
어떤 요구는 이 클래스가, 어떤 요구는 저 클래스가 이렇게 처리하는 클래스가 정해져있다는건 결국 요구하는 클라이언트가 처리하는 클래스들의 역할까지 알고있어야한다는 뜻 => 독립성이 훼손됨
떠넘긴다는건 결국 자기 일에 집중할 수 있다는 뜻
고정적인 일의 할당은 프로그램 실행 중 처리자 변경에 어렵다
결국 처리를 넘긴다는 점에서 처리할 클래스가 이미 정해져있는 것과 비교했을때, 처리가 지연된다 볼 수 있다. -> 속도냐, 처리의 유연함이냐 trade of 문제