March 10, 2022
역할 사슬 패턴은 여러 개의 객체 중에서 어떤 것이 요구를 처리할 수 있는지를 사전에 알 수 없을 때 사용한다. 요청 처리가 들어오게 되면 그것을 수신하는 객체가 자신이 처리 할 수 없는 경우에는 다음 객체에게 문제를 넘김으로써 최종적으로 요청을 처리 할 수 있는 객체의 의해 처리가 가능하도록 하는 패턴이다.
역할 사슬을 사용하는 목표는 다음과 같다.
다음 사진처럼 Client는 단순히 request를 부르기만하고, Handler를 통해서 각 Handler에 연결되어 Request를 처리한다.
프로그램의 메인 실행을 하는 부분이다.
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);
// setNext를 마치 함수 체이닝하듯이 만들어 역할사슬패턴을 구현.
alice.setNext(bob).setNext(charlie).setNext(diana).setNext(elmo).setNext(fred);
for (int i = 0; i < 500; i += 33) {
// 클라이언트의 request를 표현
alice.support(new Trouble(i));
}
}
}
각 사슬이 될 클래스들의 기본 틀이 되는 역할을 한다.
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;
}
// support 함수는 해당 request를 처리하는 함수이다.
public final void support(Trouble trouble){
// 만약 현재의 인스턴스가 resolve 할 수 있다면 done 메서드를 시랳앟ㄴ다.
if(resolve(trouble)){
done(trouble);
// 만약 현재의 인스턴스가 resolve를 할 수 없고, 다음 사슬이 존재한다면, trouble을 다음 사슬에게 넘긴다.
} else if(next != null){
next.support(trouble);
}
// 아무도 처리를 못할 경우 fail을 호출한다.
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.");
}
}
이름 그대로 resolve를 해줄 수 없는 도움되지 않는 클래스이다. 그렇기 때문에 resolve 메서드가 있을 때 항상 false를 반환한다.
public class NoSupport extends Support {
public NoSupport(String name) {
super(name);
}
// 항상 NoSupport클래스이기 때문에 false리턴한다.
protected boolean resolve(Trouble trouble) {
return false;
}
}
이 클래스는 limit를 넘어서지 않으면 resolve를 해주는 메서드이다.
public class LimitSupport extends Support {
private int limit;
public LimitSupport(String name, int limit) {
super(name);
this.limit = limit;
}
// trouble의 number가 limit보다 작다면 true를 리턴한다.
protected boolean resolve(Trouble trouble) {
if (trouble.getNumber() < limit) {
return true;
} else {
return false;
}
}
}
처음 생성될 때 주어지는 특정 숫자만 resolve 할 수 있는 Support 이다.
public class SpecialSupport extends Support {
private int number;
// number 에 대한 숫자를 저장한다.
public SpecialSupport(String name, int number) {
super(name);
this.number = number;
}
protected boolean resolve(Trouble trouble) {
// trouble의 숫자가 해당 Number와 같으면 해결할 수 있다.
if (trouble.getNumber() == number) {
return true;
} else {
return false;
}
}
}
public class OddSupport extends Support {
public OddSupport(String name) {
super(name);
}
// 홀수인 trouble만 해결해준다.
protected boolean resolve(Trouble trouble) {
if (trouble.getNumber() % 2 == 1) {
return true;
} else {
return false;
}
}
}
Client 의 request를 담당함
public class Trouble {
private int number;
public Trouble(int number) {
this.number = number;
}
public int getNumber() {
return number;
}
public String toString() {
return "[Trouble " + number + "]";
}
}
결과는 다음과 같다. Trouble에 의해 해결할 수 있는 인스턴스가 오면, 각 Support들이 자기 차례를 기다렸다가 해결한다.
the role of abstract keyword
how to chain multiple objects
the role of to String() method