14. Chain of Responsibility Pattern

2021. 11. 14. 22:20Computer Sciences/Design Patterns

문제

클라이언트가 숫자를 필터링하는 프로그램을 개발해달라고 요청했다. 그런데 단순히 하나의 조건만 있는 것이 아니라 여러 필터를 순서대로 처리하고 싶다고 한다. 즉 체인의 형태로 구성하는 필터를 원하는 것이다. 아주 간단하게 프로그래밍한다면 if 문으로 다 처리할 수 있겠지만 프로그램의 규모가 커지고 복잡해질수록 유지보수하기 힘들어진다. 그렇다면 이를 어떻게 해결해야 할까?

해결 방안

이러한 문제는 역할 사슬 패턴으로 효과적으로 처리할 수 있다. 체인 역할을 하는 클래스를 이해하기 쉽도록 필터라고 부르겠다. 필터 클래스는 연결 리스트를 구현할 때처럼 next라는 멤버 변수를 갖고 있다. 이를 통해서 체인을 구현할 수 있는 것이다. 정수기에서 물을 정수하기 위해 여러 가지 필터를 사용한다고 생각하면 이해하기 쉬울 것이다. 아래 클래스 다이어그램에서는 필터를 Support라고 명명했다. 그리고 사용할 필터들은 Support 추상 클래스를 상속받아서 각자 문제를 해결하는 방식을 resolve 추상 메서드를 오버라이딩하여 구현했다.

클래스 설명

  • Support: 필터를 구현하기 위한 추상 클래스.
  • NoSupport: 아무것도 처리하지 않는 필터
  • LimitSupport: limit 값까지의 수를 걸러내는 필터
  • OddSupport: 홀수인 수를 걸러내는 필터
  • SpecialSupport: 특정한 숫자를 걸러내는 필터
  • Trouble: 처리하고자 하는 숫자 클래스
public class Main {
    public static void main(String[] args) {

        Support a = new NoSupport("a");
        Support b = new LimitSupport("b", 100);
        Support c = new OddSupport("c");
        Support d = new LimitSupport("d", 200);
        Support e = new SpecialSupport("e", 424);
        Support f = new LimitSupport("f", 300);

                // 필터 순서 - a -> b -> c -> d -> e -> f
        a.setNext(b).setNext(c).setNext(d).setNext(e).setNext(f);

        for (int i = 0; i < 500; i++) {
            a.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;
    }

    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 "Support [name=" + name + "]";
    }

    protected abstract boolean resolve(Trouble trouble);

    protected void done(Trouble trouble) {
        System.out.println(trouble + " is resolved by " + name);
    }

    protected void fail(Trouble trouble) {
        System.out.println(trouble + " cannot be resolved");
    }
}
public class Trouble {

    private int number;

    public Trouble(int number) {
        this.number = number;
    }

    public int getNumber() {
        return number;
    }

    public String toString() {
        return "[Trouble: " + number + "]";
    }
}
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 number) {
        super(name);
        limit = number;
    }

    @Override
    protected boolean resolve(Trouble trouble) {
        if (trouble.getNumber() < limit) {
            return true;
        }
        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;
        }
        return false;
    }

}
public class SpecialSupport extends Support {

    private int number;

    public SpecialSupport(String name, int number) {
        super(name);
        this.number = number;
    }

    @Override
    protected boolean resolve(Trouble trouble) {
        if (trouble.getNumber() == number) {
            return true;
        }
        return false;
    }

}

장단점

장점

  • 요청을 보낸 쪽과 받는 쪽을 분리시킬 수 있다.
  • 객체에서는 사슬의 구조를 몰라도 되고 그 사실에 들어있는 다른 객체에 대한 직접적인 레퍼런스를 가질 필요가 없기 때문에 객체를 단순하게 만들 수 있다.
  • 사슬에 들어가는 객체를 바꾸거나 순서를 바꿈으로써 역할을 동적으로 추가/제거할 수 있다.

단점

  • 요청이 반드시 수행된다는 보장이 없다. 체인의 끝까지 갔는데도 문제를 처리해주지 않을 수 있다(이는 장점이 될 수도 있다).
  • 실행 시 과정을 디버깅하기 힘들 수 있다.

활용법

  • 윈도우 시스템에서 마우스 클릭이나 키보드 이벤트를 처리할 때 흔하게 쓰인다.

'Computer Sciences > Design Patterns' 카테고리의 다른 글

Visitor Pattern  (0) 2021.12.11
Prototype Pattern  (0) 2021.12.11
13. Bridge Pattern  (0) 2021.11.14
12. State Pattern  (0) 2021.10.15
11. Composite Pattern  (0) 2021.10.15