책임 연쇄 패턴(Chain Of Responsibility Pattern)

박세건·2024년 5월 20일
0

디자인 패턴

목록 보기
1/17
post-thumbnail

책인 연쇄 패턴이란, 클라이언트의 요청에 따른 처리를 하나의 객체가 담당하는 것이 아니라 여러 객체들로 책임을 나누고 이를 사슬(chain) 처럼 연결해서 연쇄적으로 처리하는 행동 패턴.
예를 들어서, 소비자 센터에 전화하면 음성 로봇 -> 상담원 -> 전문 기술자 순으로 각각 적합한 솔류션을 제공
처리를 담당하는 객체를 핸들러 라고 하고 예시에서는 로봇, 상담원, 기술자가 핸들러가된다.

책임 연쇄 패턴은 조건문(if-else 문 포함)의 요청 처리 로직 자체를 객체화한 것으로 생각

  • 반복되는 조건문을 하나의 Handler(interface, 추상클래스)를 생성해서 제거

사용 시기

  • 2개 이상의 객체에서 처리할때
  • 특정 순서로 핸들러를 사용해야할때

구조

  • Handler : 처리 객체를 정의하는 인터페이스
  • concreteHandler : 실제 처리하는 객체
    • 처리할 수 없다면 연결된 다은 핸들러에게 요청을 넘김

장점

  • 요청을 보내는 쪽과 요청을 처리하는 쪽을 분리하여 독립시켜 결합도를 느슨하게 만들어서 유연하게 대응
  • 각각의 핸들러들은 자신이 해야하는 일만 하기에 새로운 요청에 따른 핸들러 생성이 편리

단점

  • 핸들러들의 무한 사이클이 발생할 수 있음
  • 연결된 핸들러들이 많아질때 디버깅과 테스트가 어려움
  • 요청이 반드시 수행됨을 보장하지 않음(체인 끝까지 가도 처리가 안될 수 있음)

예시 코드

주어진 url을 여러 조건에 따라서 처리하는 코드를 하나의 코드로 작성한다면 중복되는 기능들이 많이 발생하고 중복되는 if문이 계속해서 발생
중복되는 기능을 하나의 interface로 추상화 하여 해결

개선전

class UrlParser {
    public static void run(String url) {
        // protocol 파싱
        int index = url.indexOf("://");
        if (index != -1) {
            System.out.println("PROTOCOL : " + url.substring(0, index));
        } else {
            System.out.println("NO PROTOCOL");
        }

        // domain 파싱
        int startIndex = url.indexOf("://");
        int lastIndex = url.lastIndexOf(":");

        System.out.print("DOMAIN : ");
        if (startIndex == -1) {
            if (lastIndex == -1) {
                System.out.println(url);
            } else {
                System.out.println(url.substring(0, lastIndex));
            }
        } else if (startIndex != lastIndex) {
            System.out.println(url.substring(startIndex + 3, lastIndex));
        } else {
            System.out.println(url.substring(startIndex + 3));
        }

        // port 파싱
        int index2 = url.lastIndexOf(":");
        if (index2 != -1) {
            String strPort = url.substring(index2 + 1);
            try {
                int port = Integer.parseInt((strPort));
                System.out.println("PORT : " + port);
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
        }

    }
}

개선후

// 구체적인 핸들러를 묶는 인터페이스 (추상 클래스)
abstract class Handler {
    // 다음 체인으로 연결될 핸들러
    protected Handler nextHandler = null;

    // 생성자를 통해 연결시킬 핸들러를 등록
    public Handler setNext(Handler handler) {
        this.nextHandler = handler;
        return handler; // 메서드 체이닝 구성을 위해 인자를 그대로 반환함
    }

    // 자식 핸들러에서 구체화 하는 추상 메서드
    protected abstract void process(String url);

    // 핸들러가 요청에 대해 처리하는 메서드 
    public void run(String url) {
        process(url);

        // 만일 핸들러가 연결된게 있다면 다음 핸들러로 책임을 떠넘긴다
        if (nextHandler != null)
            nextHandler.run(url);
    }
}
class ProtocolHandler extends Handler {
    @Override
    protected void process(String url) {
        int index = url.indexOf("://");
        if (index != -1) {
            System.out.println("PROTOCOL : " + url.substring(0, index));
        } else {
            System.out.println("NO PROTOCOL");
        }
    }
}

class DomianHandler extends Handler {
    @Override
    protected void process(String url) {
        int startIndex = url.indexOf("://");
        int lastIndex = url.lastIndexOf(":");

        System.out.print("DOMAIN : ");
        if (startIndex == -1) {
            if (lastIndex == -1) {
                System.out.println(url);
            } else {
                System.out.println(url.substring(0, lastIndex));
            }
        } else if (startIndex != lastIndex) {
            System.out.println(url.substring(startIndex + 3, lastIndex));
        } else {
            System.out.println(url.substring(startIndex + 3));
        }
    }
}

class PortHandler extends Handler {
    @Override
    protected void process(String url) {
        int index = url.lastIndexOf(":");
        if (index != -1) {
            String strPort = url.substring(index + 1);
            try {
                int port = Integer.parseInt((strPort));
                System.out.println("PORT : " + port);
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
        }
    }
}

참고문헌

profile
멋있는 사람 - 일단 하자

0개의 댓글