티스토리 뷰

프로그래밍

책임연쇄패턴 [ 행동 ]

수박수박좋다 2024. 7. 28. 11:57
반응형

책임연쇄 패턴 [ 행동 ]

핸들러들간의 체인에 따라 요청을 전달할 수 있게 해주는 행동 패턴

각 핸들러는 요청을 받으면 요청을 처리할지, 다음으로 전달할지를 결정한다.

 

주문시스템을 개발한다고 했을 때 초기 시스템의 동작은 순차적으로 흘러야했고 동작은 다음과 같았다.

  1. 주문확인
  2. 상품 수량확인
  3. 배송지 확인
  4. 주문 완료

시간이 지나자 각 흐름사이에 동작이 추가되어야하는 요구사항이 있었다.

  1. 주문확인
  2. 주문자 유효성확인
  3. 상품 수량확인
  4. 동일 요청 캐싱처리
  5. 배송지 확인
  6. 배송지 유효성확인
  7. 주문 완료

이런 요구사항을 만족시키기 위해 책임연쇄패턴을 이용하여 각 단계를 독립실행형 객체로 변환한다.

핸들러들을 체인으로 연결하고 각 핸들러에는 다음 핸들러에 대한 참조를 저장하기 위한 필드가 존재한다.

핸들러는 요청을 더 이상 전달하지 않고 중지시킬 수 있는 결정을 내릴 수 있다.

// Handler 인터페이스를 정의합니다.
interface Handler {
  setNext(handler: Handler): Handler;
  handle(request: any): any;
}

// 기본 핸들러 클래스입니다. 다른 핸들러들에 상속됩니다.
abstract class AbstractHandler implements Handler {
  private nextHandler: Handler | null = null;

  // 다음 핸들러를 설정합니다.
  public setNext(handler: Handler): Handler {
    this.nextHandler = handler;
    return handler;
  }

  // 요청을 처리하고, 다음 핸들러가 있으면 다음 핸들러에 요청을 전달합니다.
  public handle(request: any): any {
    if (this.nextHandler) {
      return this.nextHandler.handle(request);
    }
    return null;
  }
}

// 각 핸들러 클래스를 정의합니다.
class OrderCheckHandler extends AbstractHandler {
  public handle(request: any): any {
    console.log('주문 확인');
    return super.handle(request); // 다음 핸들러로 요청을 전달합니다.
  }
}

class CustomerValidationHandler extends AbstractHandler {
  public handle(request: any): any {
    console.log('주문자 유효성 확인');
    return super.handle(request); // 다음 핸들러로 요청을 전달합니다.
  }
}

class StockCheckHandler extends AbstractHandler {
  public handle(request: any): any {
    console.log('상품 수량 확인');
    return super.handle(request); // 다음 핸들러로 요청을 전달합니다.
  }
}

class CacheRequestHandler extends AbstractHandler {
  public handle(request: any): any {
    console.log('동일 요청 캐싱 처리');
    return super.handle(request); // 다음 핸들러로 요청을 전달합니다.
  }
}

class AddressCheckHandler extends AbstractHandler {
  public handle(request: any): any {
    console.log('배송지 확인');
    return super.handle(request); // 다음 핸들러로 요청을 전달합니다.
  }
}

class AddressValidationHandler extends AbstractHandler {
  public handle(request: any): any {
    console.log('배송지 유효성 확인');
    return super.handle(request); // 다음 핸들러로 요청을 전달합니다.
  }
}

class OrderCompleteHandler extends AbstractHandler {
  public handle(request: any): any {
    console.log('주문 완료');
    return super.handle(request); // 다음 핸들러로 요청을 전달합니다.
  }
}

// 클라이언트 코드
const orderCheckHandler = new OrderCheckHandler();
const customerValidationHandler = new CustomerValidationHandler();
const stockCheckHandler = new StockCheckHandler();
const cacheRequestHandler = new CacheRequestHandler();
const addressCheckHandler = new AddressCheckHandler();
const addressValidationHandler = new AddressValidationHandler();
const orderCompleteHandler = new OrderCompleteHandler();

// 핸들러들을 체인으로 연결합니다.
orderCheckHandler
  .setNext(customerValidationHandler)
  .setNext(stockCheckHandler)
  .setNext(cacheRequestHandler)
  .setNext(addressCheckHandler)
  .setNext(addressValidationHandler)
  .setNext(orderCompleteHandler);

// 요청을 첫 번째 핸들러에 전달합니다.
orderCheckHandler.handle({});

장점

  1. 유연성: 요청을 처리하는 객체들을 쉽게 추가하거나 제거할 수 있음. 새로운 요청 처리 객체를 추가하거나 기존 객체를 제거하는 것이 간단함.
  2. 단일 책임 원칙(SRP) 준수: 각 객체는 하나의 작업만을 처리함. 이를 통해 코드의 가독성과 유지보수성이 향상됨.
  3. 확장성: 새로운 처리 단계를 추가할 때 기존 코드를 수정할 필요 없이 체인에 추가하기만 하면 됨. 시스템의 확장성을 높여줌.
  4. 클라이언트와 처리 객체의 분리: 클라이언트는 요청을 처리하는 객체들에 대해 알 필요가 없음. 클라이언트는 단지 체인의 첫 번째 객체에 요청을 전달하기만 하면 됨.
  5. 유연한 처리 순서: 처리 객체의 순서를 쉽게 변경할 수 있음. 다양한 처리 시나리오를 지원할 수 있음.

단점

  1. 디버깅 어려움: 체인 구조로 인해 디버깅이 어려울 수 있음. 요청이 어떤 객체에 의해 처리되었는지 추적하기가 복잡할 수 있음.
  2. 성능 문제: 요청이 체인의 끝까지 전달되는 경우, 많은 객체들을 거치면서 성능이 저하될 수 있음. 특히 체인이 길어질수록 더 큰 문제가 됨.
  3. 불확실한 처리: 모든 객체가 요청을 처리하지 않고 다음 객체로 전달할 수 있음. 요청이 반드시 처리된다는 보장이 없음. 요청이 체인의 끝에 도달할 때까지 처리되지 않을 수 있음.
  4. 복잡성 증가: 체인의 구조가 복잡해질수록 시스템의 전반적인 복잡성이 증가함. 이는 코드의 이해와 유지보수를 어렵게 만들 수 있음.
반응형

'프로그래밍' 카테고리의 다른 글

rust-0  (1) 2024.09.08
추상팩토리 패턴 [ 생성 ]  (0) 2024.07.28
프록시 패턴  (0) 2024.07.19
브릿지 패턴  (0) 2024.07.19
싱글톤 패턴  (0) 2024.07.11
댓글
반응형
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
농담곰의 고군분투 개발기