Chain Of Responsibility 패턴은 책임의 사슬이라고 번역할 수 있는 패턴이다. 여러 객체가 각기 다른 객체의 맴버 객체로 연결이 되어 있고(그 구조는 상관이 없다. 선형일 수도 있고 트리일 수도 있다.), 어떤 작업에 대한 요청이 발생했을 때 스스로 해결할 수 있을 경우에만 그 작업에 대해 직접 수행하고, 그렇지 않은 경우 맴버 객체에게 작업을 넘긴다.
패턴의 구조가 명확하지 않은 만큼 구현의 방법 또한 다양하다. 일반적으로는 사슬을 구성하는 상위 타입은 동일하게 가져가고, 개별 동작은 하위 타입이 구현하는 형태를 띈다. 하지만 하위 타입 구현 없이 동일한 타입만으로 사슬을 구성하는 것이 가능하고, Composite 패턴과 같이 트리 형태의 사슬을 구성하는 것 또한 가능하다.
Chain Of Responsibility 패턴 클래스 다이어그램
Chain Of Responsibility 패턴의 구현
abstract class Boundary{
protected int upper;
protected int lower;
protected Boundary nested = null;
public void setNested(Boundary nested){ this.nested = nested; }
public Boundary(int upper, int lower){
this.upper = upper;
this.lower = lower;
}
public void action(int value){
if(isInBoundary(value) == true) individualAction();
else if(nested != null) nested.action(value);
else individualAction();
}
abstract protected void individualAction();
private boolean isInBoundary(int value){
if(value >= lower && value <= upper) return true;
return false;
}
}
class NormalVoltage extends Boundary{
public NormalVoltage(int upper, int lower){
super(upper, lower);
}
protected void individualAction(){
System.out.println("normal operation");
}
}
class WarningVoltage extends Boundary{
public WarningVoltage(int upper, int lower){
super(upper, lower);
}
protected void individualAction(){
System.out.println("warning operation");
}
}
class FaultVoltage extends Boundary{
public FaultVoltage(int upper, int lower){
super(upper, lower);
}
protected void individualAction(){
System.out.println("fault operation");
}
}
우선 책임의 사슬 패턴을 위해서 사슬을 구성하는 Boundary라는 상위 클래스를 선언한다. 이 클래스는 책임 사슬을 구성할 수 있도록 setNested() 메소드를 제공한다. 이 메소드는 동일한 Boundary 객체를 받아 맴버 객체로 설정해 준다. 만약 어떤 객체가 작업을 수행할 조건에 맞지 않으면 맴버 객체로 설정된 Boundary 객체에게 작업을 위임한다.
하위 클래스에는 3종류가 있다. 우선 정상 범위의 전압을 나타내는 NormalVoltage 클래스가 있고, 경고 상태와 고장 상태를 나타내는 WarningVoltage와 FaultVoltage 클래스가 있다. 이들 클래스는 각각 자신이 작업을 수행해야 할 경우에 호출될 individualAction() 메소드를 재정의 하고 있다.
아래 main() 메소드에서는 이들간의 관계를 설정하고 동작시키는 코드가 있다.
실행 방법
public static void main(String[] args) {
Boundary voltage = new NormalVoltage(230, 210);
Boundary warning = new WarningVoltage(240, 200);
Boundary fault = new FaultVoltage(Integer.MAX_VALUE, Integer.MIN_VALUE);
voltage.setNested(warning);
warning.setNested(fault);
voltage.action(220);
voltage.action(235);
voltage.action(245);
}
기본적으로 NormalVoltage 객체가 가장 바깥쪽에 있고, WarningVoltage 객체가 그 다음, 가장 안쪽에는 FaultVoltage 객체가 있다. action() 메소드를 통해 입력되는 입력 값을 최초로 받는 것은 가장 바깥쪽 객체인 NormalVoltage 객체이다. 이 객체는 상위 객체인 Boundary 객체가 정한 바와 같이 우선 입력된 값이 자신의 범위에 맞는지를 확인한다. 만약 맞으면 individualAction() 메소드를 호출하여 자신이 작업을 수행한다. 만약 맞지 않는다면 nested 객체가 있는지를 확인하고 있으면 작업을 위임하기 위해 nested 객체의 action() 메소드를 호출한다. 만약 nested 객체가 없다면 자신이 최종 작업 수행자이므로 자신의 individualAction() 메소드를 수행한다.
다시 이야기 하지만 이 예제는 일반적인 형태이긴 하나 꼭 하위 객체를 생성해야 하는 것은 아니다. 오히려 이 예제와 같은 경우라면 하위 객체를 생성하는 것보다는 Boundary 객체를 범용적으로 활용할 수 있도록 만드는 편이 좋다. 그렇게 하면 책임 사슬을 좀 더 유연하게 늘이거나 줄일 수 있다. 경고 레벨을 늘리고자 할 경우 그냥 Boundary 객체를 중간에 하나씩 추가해 주기만 하면 된다. 그러면 좀 더 촘촘한 간격으로 경고와 고장을 나타낼 수 있게 된다. 이렇게 하는 것이 Chain Of Responsibility 패턴을 사용하는 목적인 유연성을 좀 더 반영할 수 있을 것이다.
'5.디자인패턴' 카테고리의 다른 글
Command 패턴 (0) | 2016.09.18 |
---|---|
Flyweight 패턴 (0) | 2016.09.18 |
Composite 패턴 (0) | 2016.09.17 |
Iterator 패턴 (0) | 2016.09.17 |
Enum Abstract Factory 패턴 (0) | 2016.09.16 |