본문 바로가기
프로그래밍

책임 연쇄 설계 패턴

by it-view 2021. 12. 30.
반응형

이 설계 패턴은 행동 설계 패턴으로 분류됩니다. 책임 사슬의 주된 목적은 느슨하게 결합하는 것인데, 예를 들어 송신자와 수신자 사이의 통신을 고려해보자, 송신자는 송신자가 누구인지 모르고 수신자는 발신자가 누구인지 알지 못한다. 이는 단순히 이 설계 패턴이 요청 유형에 따라 요청의 발신자와 수신자를 분리한다는 것을 의미합니다.

개발자가 연쇄 책임 패턴을 사용할 수 있는 상황입니까?

· 개발자가 요청의 송신자와 수신자를 분리하려는 경우

· 런타임에 결정되는 여러 개체가 요청을 처리할 수 있는 후보입니다.

 

· 개발자가 코드에서 처리기를 명시적으로 지정하지 않으려는 경우

· 수신기를 명시적으로 지정하지 않고 개발자가 여러 개체 중 하나에 요청을 실행하려는 경우.

위의 UML 다이어그램에서 볼 수 있듯이 이 설계 패턴에서는 핸들러라는 클래스를 사용하여 송신기와 수신기를 분리합니다.

예:

 

항공사가 비행 시간에 기초하여 항공기의 연료 수요를 계산하는 연료량 계획을 만드는 소프트웨어가 필요하다고 가정하자.

1단계: 처리기 추상 클래스를 구현합니다.

핸들러 클래스에서 후계자를 보유하는 참조 변수를 만들고 다음과 같은 추상 메서드를 만듭니다.

"최대 연료량 계산(연료량 계획 연료량 계획)"

package ChainOfResponsibilityExample;
public abstract class FuelQuantityHandler {
  protected FuelQuantityHandler successor;
  public void setSuccessor(FuelQuantityHandler successor) {
            this.successor = successor;
  }
  public abstract float calculateMaxFuelQuantity (FuelQuantityPlan fuelQuantityPlan);
}
 

참고: 또한 이 클래스는 추상 클래스로 구현되지 않고 인터페이스 클래스로 만들 수 있습니다. 하지만 이 예에서는 추상적인 수업으로 구현했습니다.

2단계: 연료량 계획 클래스를 만들고 인스턴스(instance) 변수를 선언하며 적절한 생성자, 설정자 및 게터를 구현합니다.

package ChainOfResponsibilityExample;
public class FuelQuantityPlan {
      private String flightPath;
      private float flyingHours;
      private float fuelQuantity;
  public void setFuelQuantity(float fuelQuantity) {
            this.fuelQuantity = fuelQuantity;
  }
  public FuelQuantityPlan(String flightPath, float flyingHours) {
            this.flightPath = flightPath;
            this.flyingHours = flyingHours;
  }
  public String getFlightPath() {
            return flightPath;
  }
  public float getFlyingHours() {
            return flyingHours;
  }
  public float getFuelQuantity() {
            return fuelQuantity;
  }
}

3단계: 수신기 클래스를 만들고 처리기 클래스를 부모 클래스로 만듭니다.

참고: 문서의 복잡성과 길이를 줄이기 위해 첫 번째 수신기 클래스와 마지막 수신기 클래스만 여기에 설명되어 있습니다.

 
package ChainOfResponsibilityExample;
public class ShortRange extends FuelQuantityHandler{
      @Override
      public float calculateMaxFuelQuantity(FuelQuantityPlan fuelQuantityPlan) {
                fuelQuantityPlan.setFuelQuantity((float) (fuelQuantityPlan.getFlyingHours() * 0.2));
                System.out.println("Short Range Fuel Quantity Added");
                if ( fuelQuantityPlan.getFlyingHours() <= 5) {
                              return fuelQuantityPlan.getFuelQuantity();
                } else {
                              return successor.calculateMaxFuelQuantity(fuelQuantityPlan);
                }
      }
}
package ChainOfResponsibilityExample;
public class IntercontinentalRange extends FuelQuantityHandler{
      @Override
      public float calculateMaxFuelQuantity(FuelQuantityPlan fuelQuantityPlan) {
                fuelQuantityPlan.setFuelQuantity((float) (fuelQuantityPlan.getFuelQuantity() + (fuelQuantityPlan.getFlyingHours() - 12.5) * 0.8));
                System.out.println("Intercontinental Range Fuel Quantity Added");
                return fuelQuantityPlan.getFuelQuantity();
      }
}

먼저 관련 값을 계산한 후, 해당 값이 다음 연료량 계산에 적합한지 확인한 후 연료량 계획 등급의 현재 인스턴스는 후속 인스턴스의 "calculateMaxFuelQuantity" 방법에 대한 인수로 통과된다.

리시버 등급에 해당하는 마지막 등급에서는 최종 연료량만 계산하고 연료량만 넘기면 됩니다.

4단계: 책임 설계 패턴의 연쇄를 시작할 초기 클래스를 만듭니다.

참고: 이것은 강제적인 단계가 아니며, 개발자는 첫 번째 수신기 클래스를 사용하여 책임 체인을 시작할 수 있습니다.

 
package ChainOfResponsibilityExample;
public class MinimumFuelQuantity extends FuelQuantityHandler{
      @Override
      public float calculateMaxFuelQuantity(FuelQuantityPlan fuelQuantityPlan) {
                return successor.calculateMaxFuelQuantity(fuelQuantityPlan);
      }
}

5단계: 주요 방법을 만들고 책임 연쇄 설계 패턴이 어떻게 작동하는지 확인합니다.

package ChainOfResponsibilityExample;
public class Application {
      public static void main(String[] args) {
                ShortRange shortRange = new ShortRange();
                MidRange midRange = new MidRange();
                LongRange longRange = new LongRange();
                IntercontinentalRange intercontinentalRange = new IntercontinentalRange();
        MinimumFuelQuantity minimumFuelQuantity = new MinimumFuelQuantity();
        minimumFuelQuantity.setSuccessor(shortRange);
                shortRange.setSuccessor(midRange);
                midRange.setSuccessor(longRange);
                longRange.setSuccessor(intercontinentalRange);
        FuelQuantityPlan fuelQuantityPlan = new FuelQuantityPlan("AB1", 2.0f);
                System.out.println(minimumFuelQuantity.calculateMaxFuelQuantity(fuelQuantityPlan));
        FuelQuantityPlan fuelQuantityPlan1 = new FuelQuantityPlan("AB2", 6.0f);
                System.out.println(minimumFuelQuantity.calculateMaxFuelQuantity(fuelQuantityPlan1));
        FuelQuantityPlan fuelQuantityPlan2 = new FuelQuantityPlan("AB3", 8.0f);
                System.out.println(minimumFuelQuantity.calculateMaxFuelQuantity(fuelQuantityPlan2));
        FuelQuantityPlan fuelQuantityPlan3 = new FuelQuantityPlan("AB4", 14.0f);
                System.out.println(minimumFuelQuantity.calculateMaxFuelQuantity(fuelQuantityPlan3));
      }
}

이 클래스에서 초기 클래스를 포함한 모든 수신기 클래스 인스턴스를 선언하고 초기화합니다.

모든 수신기 클래스에 대한 후속 버전을 설정합니다.

 

책임 연쇄 설계 패턴의 장점:

· 개발자는 핵심 구현을 조작하지 않고 수신자와 송신자의 구조나 순서를 변경할 수 있다. 그 결과 객체 할당 업무의 유연성이 향상됩니다.

책임 연쇄 설계 패턴의 단점:

· 관련 수신기로 객체를 수신하는 것에 대해서는 보장되지 않습니다.

· 시스템의 성능이 영향을 받지만 코드 디버깅에서도 쉬운 작업이 아닙니다.

 

책임디자인 패턴의 사슬이 무엇인지, 책임의 사슬을 어떻게 구현할지, 언제 사용할지, 장단점이 무엇인지 이제 이해하셨으면 합니다.

댓글