요청을 처리할 수 있는 객체들을 연결한 체인을 생성하고, 요청이 체인을 따라 전달되며 각 객체가 해당 요청을 처리하거나 다음 객체로 전달하는 패턴이다. 책임을 하나의 객체에 할당하는 것이 아닌 객체 집합에 요청을 전달하는 것이다.
돈이 입력되었을 100원 50원 10원짜리 동전의 개수를 구해야 하는 상황이 있다 가정하자.
1. 동전의 개수를 세는 Change클래스는 다음과 같다
public class Change {
public void getChange(int amount) {
int numOf100 = amount / 100;
amount %= 100;
int numOf50 = amount / 50;
amount %= 50;
int numOf10 = amount / 10;
amount %= 10;
System.out.println("100원: " + numOf100);
System.out.println("50원: " + numOf50);
System.out.println("10원: " + numOf10);
}
}
위 구조는 새로운 동전의 종류를 추가하거나 동전의 종류를 수정할 때 Change클래스를 수정해야 한다. 즉, OCP를 위배하는 코드라는 뜻이다.
동전 별로 개수를 구하지 말고 '동전 집단'에 개수를 구하도록 구조를 바꾸어보자
1. Coin
public abstract class Coin {
protected Coin nextCoin;
protected int value;
public void setNextCoin(Coin nextCoin) {
this.nextCoin = nextCoin;
}
public void process(int amount) {
if (amount >= value) {
int num = amount / value;
int remainder = amount % value;
System.out.println(value + "원: " + num);
if (remainder != 0) nextCoin.process(remainder);
} else {
nextCoin.process(amount);
}
}
}
handleRequest에 해당하는 process는 이를 상속받는 서브 클래스에서 재정의하는 것도 하나의 방법이지만 Coin클래스에 단위에 해당하는 value를 정의함으로 process과정을 일반화 시켰다.
2. Coin 100, 50, 10
public class Coin100 extends Coin {
public Coin100() {
this.value = 100;
}
}
public class Coin50 extends Coin {
public Coin50() {
this.value = 50;
}
}
public class Coin10 extends Coin {
public Coin10() {
this.value = 10;
}
}
3. Client
public class Client {
public static void main(String[] args) {
Coin coin100 = new Coin100();
Coin coin50 = new Coin50();
Coin coin10 = new Coin10();
coin100.setNextCoin(coin50);
coin50.setNextCoin(coin10);
coin100.process(260);
}
}
Client라 하였지만 coin100, 50, 10을 사용하는 다른 클래스가 추가된다면 그 클래스에 coin100객체를 넘겨주면 된다.
Handler집단에 어떤 Handler가 요청을 처리하는지는 Client는 모른다. 단지, Handler집단에 있는 어떤 객체가 요청을 처리한다는 사실만 알 수 있다.
Client의 요청을 처리할 수 없는 상황이 발생한다면 에러상황을 reply해줄 방법을 정의해주어야 한다.