자바 디자인 패턴: 체인 오브 리스폰시빌리티 패턴으로 요청 처리

자바 디자인 패턴 소개

자바는 객체지향 프로그래밍 언어로, 객체지향 설계를 위한 디자인 패턴을 지원합니다. 디자인 패턴은 소프트웨어 개발에서 자주 발생하는 문제를 해결하기 위한 일반적인 해결책으로, 이전에 검증된 설계 방법을 재사용하는 것입니다.

디자인 패턴은 크게 생성 패턴, 구조 패턴, 행동 패턴으로 분류됩니다. 생성 패턴은 객체 인스턴스를 만드는 방법에 대한 패턴입니다. 구조 패턴은 클래스들과 객체들을 조합해 더 큰 구조를 만드는 방법에 대한 패턴입니다. 행동 패턴은 객체들이 서로 상호작용하는 방법에 대한 패턴입니다.

이번 글에서는 행동 패턴 중 체인 오브 리스폰시빌리티 패턴에 대해 알아보겠습니다.

체인 오브 리스폰시빌리티 패턴의 개념과 특징

체인 오브 리스폰시빌리티(Chain of Responsibility) 패턴은 요청을 처리할 수 있는 객체들의 연결을 만들어, 요청을 보내는 객체와 요청을 처리할 객체들을 분리하는 패턴입니다.

이 패턴에서는 객체들이 연쇄적으로 요청을 처리하며, 각 객체는 요청을 처리할 수 없으면 다음 객체로 요청을 전달합니다. 이는 요청에 대한 처리를 담당하는 객체를 동적으로 변경해야 할 때 유용합니다.

체인 오브 리스폰시빌리티 패턴은 다음과 같은 특징을 가집니다.

  • 객체 간 결합도를 낮춥니다.
  • 요청 처리 객체를 동적으로 변경할 수 있습니다.
  • 요청 처리 객체 간에 투명성을 유지합니다.

자바에서 체인 오브 리스폰시빌리티 패턴 구현 방법

자바에서 체인 오브 리스폰시빌리티 패턴을 구현하는 방법은 다음과 같습니다.

  1. 요청 객체(Handler)를 정의합니다. 이 객체는 요청을 처리할 수 있는 메서드를 정의하고, 다음 요청 처리 객체를 설정하는 메서드를 추가합니다.
public abstract class Handler {
    protected Handler successor;

    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }

    public abstract void handleRequest(Request request);
}
  1. 요청 객체를 상속받아 실제 요청을 처리하는 객체를 정의합니다.
public class ConcreteHandlerA extends Handler {
    @Override
    public void handleRequest(Request request) {
        if (request.getType() == RequestType.TYPE_A) {
            System.out.println("Request handled by ConcreteHandlerA");
        } else if (successor != null) {
            successor.handleRequest(request);
        }
    }
}

public class ConcreteHandlerB extends Handler {
    @Override
    public void handleRequest(Request request) {
        if (request.getType() == RequestType.TYPE_B) {
            System.out.println("Request handled by ConcreteHandlerB");
        } else if (successor != null) {
            successor.handleRequest(request);
        }
    }
}
  1. 요청을 생성하고 체인의 시작점에 있는 요청 처리 객체에 요청을 보냅니다.
Handler handlerA = new ConcreteHandlerA();
Handler handlerB = new ConcreteHandlerB();
handlerA.setSuccessor(handlerB);

Request requestA = new Request(RequestType.TYPE_A);
handlerA.handleRequest(requestA);

Request requestB = new Request(RequestType.TYPE_B);
handlerA.handleRequest(requestB);

요청 처리에서 체인 오브 리스폰시빌리티 패턴의 활용 예시

체인 오브 리스폰시빌리티 패턴은 요청 처리에서 다양한 상황에서 활용될 수 있습니다.

예를 들어, 서버에서 클라이언트로 오는 요청을 처리하는 경우, 요청을 처리할 객체를 동적으로 변경해야 할 때 체인 오브 리스폰시빌리티 패턴을 사용할 수 있습니다. 이를 통해 서버의 부하를 분산하거나, 요청에 따른 다양한 처리 방법을 선택할 수 있습니다.

또한, 로깅 시스템에서도 체인 오브 리스폰시빌리티 패턴을 활용할 수 있습니다. 로그 정보를 처리하는 객체를 연결하여, 로그를 처리할 수 있는 객체를 선택적으로 사용할 수 있습니다.

체인 오브 리스폰시빌리티 패턴은 객체 간의 결합도를 낮추고, 객체를 동적으로 변경할 수 있는 유연성을 제공합니다. 이를 통해 요청 처리 시스템을 보다 유연하게 설계할 수 있습니다.

자바 디자인 패턴: 체인 오브 리스폰시빌리티 패턴으로 요청 처리

자바 디자인 패턴: 체인 오브 리스폰시빌리티 패턴으로 요청 처리

자바 프로그램에서 요청 처리는 매우 중요한 작업이며, 이를 효율적으로 처리하기 위해서는 디자인 패턴을 사용할 필요가 있습니다. 디자인 패턴 중 체인 오브 리스폰시빌리티 패턴은 요청 처리를 순차적으로 처리할 수 있도록 구조화된 패턴입니다. 이번 글에서는 체인 오브 리스폰시빌리티 패턴의 구조와 원리, 그리고 자바 어플리케이션에서의 활용 방법과 예제를 살펴보겠습니다.

자바 디자인 패턴: 체인 오브 리스폰시빌리티 패턴 소개

체인 오브 리스폰시빌리티 패턴은 요청 처리를 순차적으로 처리하는 구조를 갖는 디자인 패턴입니다. 이 패턴은 요청 처리를 위한 객체들을 연결하여 체인 형태로 만들고, 각 객체가 요청을 처리할 수 있는지 여부를 판단하여 처리할 수 있으면 처리하고, 처리할 수 없으면 다음 객체로 요청을 전달하는 방식으로 동작합니다.

체인 오브 리스폰시빌리티 패턴은 요청 처리 시에 객체 간의 결합도를 낮추고, 객체의 역할을 분리하여 유연성을 높일 수 있습니다. 또한 새로운 요청 처리 객체를 추가하거나 기존 객체를 수정하여 요청 처리 방식을 변경하는 경우에도 코드 수정을 최소화할 수 있습니다.

체인 오브 리스폰시빌리티 패턴은 다음과 같은 구조로 이루어져 있습니다.

chain-of-responsibility-pattern

  • Handler: 요청 처리를 담당하는 추상 클래스 또는 인터페이스. 실제 요청 처리 객체는 이 클래스를 상속하여 구현합니다.
  • ConcreteHandler: 실제 요청 처리 객체. 요청을 처리할 수 있으면 처리하고, 처리할 수 없으면 다음 객체로 요청을 전달합니다.
  • Client: 요청을 생성하고, 체인의 시작 객체를 생성합니다.

체인 오브 리스폰시빌리티 패턴의 구조와 원리

체인 오브 리스폰시빌리티 패턴은 요청 처리 객체를 연결하여 체인 형태로 만들고, 각 객체가 요청을 처리할 수 있는지 여부를 판단하여 처리할 수 있으면 처리하고, 처리할 수 없으면 다음 객체로 요청을 전달하는 방식으로 동작합니다.

요청 처리 객체를 연결하는 것은 다음과 같이 구현할 수 있습니다.

public abstract class Handler {
    private Handler next;

    public void setNext(Handler next) {
        this.next = next;
    }

    public Handler getNext() {
        return next;
    }

    public abstract void handleRequest(Request request);
}

public class ConcreteHandler1 extends Handler {
    @Override
    public void handleRequest(Request request) {
        if (request.getType() == RequestType.TYPE1) {
            System.out.println("ConcreteHandler1 handles request: " + request.getName());
        } else if (getNext() != null) {
            getNext().handleRequest(request);
        } else {
            System.out.println("No handler found for request: " + request.getName());
        }
    }
}

public class ConcreteHandler2 extends Handler {
    @Override
    public void handleRequest(Request request) {
        if (request.getType() == RequestType.TYPE2) {
            System.out.println("ConcreteHandler2 handles request: " + request.getName());
        } else if (getNext() != null) {
            getNext().handleRequest(request);
        } else {
            System.out.println("No handler found for request: " + request.getName());
        }
    }
}

public class Request {
    private RequestType type;
    private String name;

    public Request(RequestType type, String name) {
        this.type = type;
        this.name = name;
    }

    public RequestType getType() {
        return type;
    }

    public String getName() {
        return name;
    }
}

public enum RequestType {
    TYPE1, TYPE2
}

public class Client {
    public static void main(String[] args) {
        Handler handler1 = new ConcreteHandler1();
        Handler handler2 = new ConcreteHandler2();
        handler1.setNext(handler2);

        Request request1 = new Request(RequestType.TYPE1, "Request 1");
        Request request2 = new Request(RequestType.TYPE2, "Request 2");

        handler1.handleRequest(request1);
        handler1.handleRequest(request2);
    }
}

위 코드는 체인 오브 리스폰시빌리티 패턴의 구조를 보여줍니다. Handler 클래스는 요청 처리를 담당하는 추상 클래스 또는 인터페이스로, ConcreteHandler 클래스는 실제 요청 처리 객체입니다. Client 클래스는 요청을 생성하고, 체인의 시작 객체를 생성합니다.

체인의 시작 객체인 handler1 객체는 ConcreteHandler1 객체를 다음 객체로 설정하고, ConcreteHandler1 객체는 ConcreteHandler2 객체를 다음 객체로 설정합니다. 이렇게 객체를 연결하면 요청이 전달될 때 체인 형태로 전달되며, 각 객체는 자신이 처리할 수 있는 요청인지 판단하여 처리할 수 있으면 처리하고, 처리할 수 없으면 다음 객체로 요청을 전달합니다.

자바 어플리케이션에서 체인 오브 리스폰시빌리티 패턴 활용

자바 어플리케이션에서 체인 오브 리스폰시빌리티 패턴은 요청 처리를 효율적으로 구현할 수 있는 방법입니다. 예를 들어, 웹 어플리케이션에서 요청을 처리하는 경우에는 체인 형태로 처리할 수 있습니다.

public abstract class Handler {
    private Handler next;

    public void setNext(Handler next) {
        this.next = next;
    }

    public Handler getNext() {
        return next;
    }

    public abstract void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
}

public class AuthenticationHandler extends Handler {
    @Override
    public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        if (request.getUserPrincipal() == null) {
            response.sendRedirect("/login");
        } else if (getNext() != null) {
            getNext().handleRequest(request, response);
        }
    }
}

public class AuthorizationHandler extends Handler {
    @Override
    public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        if (request.isUserInRole("admin")) {
            if (getNext() != null) {
                getNext().handleRequest(request, response);
            }
        } else {
            response.sendRedirect("/access-denied");
        }
    }
}

public class RequestHandler extends Handler {
    @Override
    public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // Handle the request
    }
}

public class Application {
    public static void main(String[] args) {
        Handler authenticationHandler = new AuthenticationHandler();
        Handler authorizationHandler = new AuthorizationHandler();
        Handler requestHandler = new RequestHandler();

        authenticationHandler.setNext(authorizationHandler);
        authorizationHandler.setNext(requestHandler);

        // Create a servlet filter that uses the Chain of Responsibility pattern
        Filter chainFilter = new Filter() {
            @Override
            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
                authenticationHandler.handleRequest((HttpServletRequest) request, (HttpServletResponse) response);
            }
        };
    }
}

위 코드는 웹 어플리케이션에서 체인 오브 리스폰시빌리티 패턴을 활용한 예제입니다. AuthenticationHandler는 인증 처리, AuthorizationHandler는 권한 처리, RequestHandler는 실제 요청 처리를 담당합니다. 이렇게 각 역할을 분리하고, 객체를 연결하여 체인 형태로 처리하면 요청 처리를 효율적으로 구현할 수 있습니다.

체인 오브 리스폰시빌리티 패턴을 활용한 요청 처리 예제

체인 오브 리스폰시빌리티 패턴을 활용하여 요청 처리를 구현하는 예제를 살펴보겠습니다.

public abstract class Handler {
    private Handler next;

    public void setNext(Handler next) {
        this.next = next;
    }

    public Handler getNext() {
        return next;
    }

    public abstract void handleRequest(Request request);
}

public class ConcreteHandler1 extends Handler {
    @Override
    public void handleRequest(Request request) {
        if (request.getType() == RequestType.TYPE1) {
            System.out.println("ConcreteHandler1 handles request: " + request.getName());
        } else if (getNext() != null) {
            getNext().handleRequest(request);
        } else {
            System.out.println("No handler found for request: " + request.getName());
        }
    }
}

public class ConcreteHandler2 extends Handler {
    @Override
    public void handleRequest(Request request) {
        if (request.getType() == RequestType.TYPE2) {
            System.out.println("ConcreteHandler2 handles request: " + request.getName());
        } else if (getNext() != null) {
            getNext().handleRequest(request);
        } else {
            System.out.println("No handler found for request: " + request.getName());
        }
    }
}

public class Request {
    private RequestType type;
    private String name;

    public Request(RequestType type, String name) {
        this.type = type;
        this.name = name;
    }

    public RequestType getType() {
        return type;
    }

    public String getName() {
        return name;
    }
}

public enum RequestType {
    TYPE1, TYPE2
}

public class Client {
    public static void main(String[] args) {
        Handler handler1 = new ConcreteHandler1();
        Handler handler2 = new ConcreteHandler2();
        handler1.setNext(handler2);

        Request request1 = new Request(RequestType.TYPE1, "Request 1");
        Request request2 = new Request(RequestType.TYPE2, "Request 2");

        handler1.handleRequest(request1);
        handler1.handleRequest(request2);
    }
}

위 코드는 체인 오브 리스폰시빌리티 패턴을 활용하여 요청 처리를 구현한 예제입니다. ConcreteHandler1은 RequestType.TYPE1 요청을 처리하고, ConcreteHandler2는 RequestType.TYPE2 요청을 처리합니다. 이렇게 객체를 연결하면 요청이 전달될 때 체인 형태로 전달되며, 각 객체는 자신이 처리할 수 있는 요청인지 판단하여 처리할 수 있으면 처리하고, 처리할 수 없으면 다음 객체로 요청을 전달합니다.

위 예제를 실행하면 다음과 같은 결과가 출력됩니다.

ConcreteHandler1 handles request: Request 1
ConcreteHandler2 handles request: Request 2

결론

체인 오브 리스폰시빌리티 패턴은 요청 처리를 효율적으로 구현할 수 있는 디자인 패턴입니다. 객체 간의 결합도를 낮추고, 객체의 역할을 분리하여 유연성을 높일 수 있습니다. 또한 새로운 요청 처리 객체를 추가하거나 기존 객체를 수정하여 요청 처리 방식을 변경하는 경우에도 코드 수정을 최소화할 수 있습니다. 자바 어플리케이션에서 체인 오브 리스폰시빌리티 패턴을 활용하면 요청 처리를 효율적으로 구현할 수 있으며, 이를 위해 Handler, ConcreteHandler, Client 클래스를 구현하는 방법을 살펴보았습니다.