책임 연쇄 패턴
개념
책임 연쇄 패턴(Chain of Responsibility): 여러 객체가 요청을 처리할 기회를 가지도록 하여 요청 발신자와 수신자 간의 결합도를 줄인다. 이러한 객체들을 하나의 체인으로 연결하고, 요청을 이 체인을 따라 전달하여 첫 번째로 요청을 처리하는 객체가 이를 처리할 때까지 계속 전달한다.
책임 연쇄의 장점: 고객이 요청을 제출하면 요청은 체인을 따라 전달되어 ConcreteHandler 객체가 처리한다. 수신자와 발신자는 서로에 대한 구체적인 정보를 갖고 있지 않으며, 체인의 각 객체는 체인 구조를 알지 못한다. 결과적으로 책임 연쇄는 객체 간의 연결을 단순화하며, 각 객체는 다음 처리자에 대한 참조만 유지하면 된다. 요청을 처리하는 구조를 언제든지 추가하거나 수정할 수 있어 객체에 대한 책임 할당의 유연성을 높인다. 주의할 점은 요청이 체인의 끝까지 도달했지만 처리되지 않거나, 잘못된 구성으로 인해 처리되지 않을 수 있다는 것이다.
예제
관리자들이 휴가 및 급여 인상 요청을 처리하는 시나리오
package com.gof.chainOfResponsibility;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 요청 정보를 담는 클래스
*
* @since 2023-03-01
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Application {
private String category;
private int days;
private String reason;
}
package com.gof.chainOfResponsibility;
/**
* 관리자의 추상 클래스
*
* @since 2023-03-01
*/
public abstract class Supervisor {
private String supervisorName;
public Supervisor(String name) {
this.supervisorName = name;
}
private Supervisor nextLevel;
public void setNextLevel(Supervisor level) {
this.nextLevel = level;
}
public Supervisor getNextLevel() {
return nextLevel;
}
public String getName() {
return supervisorName;
}
public abstract void process(Application application);
}
package com.gof.chainOfResponsibility;
import java.util.Objects;
/**
* 일반 관리자 클래스
*
* @since 2023-03-01
*/
public class RegularManager extends Supervisor {
public RegularManager(String name) {
super(name);
}
@Override
public void process(Application application) {
if (application.getCategory().equals("휴가") && application.getDays() <= 2) {
System.out.printf("%s: %s %d 일, 승인됨.\n", getName(), application.getReason(), application.getDays());
} else {
if (Objects.nonNull(getNextLevel())) {
getNextLevel().process(application);
}
}
}
}
package com.gof.chainOfResponsibility;
import java.util.Objects;
/**
* 주임 관리자 클래스
*
* @since 2023-03-01
*/
public class DeputyManager extends Supervisor {
public DeputyManager(String name) {
super(name);
}
@Override
public void process(Application application) {
if (application.getCategory().equals("휴가") && application.getDays() < 5) {
System.out.printf("%s: %s %d 일, 승인됨.\n", getName(), application.getReason(), application.getDays());
} else {
if (Objects.nonNull(getNextLevel())) {
getNextLevel().process(application);
}
}
}
}
package com.gof.chainOfResponsibility;
/**
* 총괄 관리자 클래스
*
* @since 2023-03-01
*/
public class ExecutiveManager extends Supervisor {
public ExecutiveManager(String name) {
super(name);
}
@Override
public void process(Application application) {
if (application.getCategory().equals("휴가")) {
System.out.printf("%s: %s %d 일, 승인됨.\n", getName(), application.getReason(), application.getDays());
} else if (application.getCategory().equals("급여 인상")) {
if (application.getDays() <= 500) {
System.out.printf("%s: %s %d 원, 승인됨.\n", getName(), application.getReason(), application.getDays());
} else {
System.out.printf("%s: %s %d 원, 검토 필요.\n", getName(), application.getReason(), application.getDays());
}
}
}
}
package com.gof.chainOfResponsibility;
/**
* 클라이언트 코드
*
* @since 2023-03-01
*/
public class DemoClient {
public static void main(String[] args) {
// 관리자 계층 구성
RegularManager regular = new RegularManager("팀장");
DeputyManager deputy = new DeputyManager("부서장");
ExecutiveManager executive = new ExecutiveManager("이사");
regular.setNextLevel(deputy);
deputy.setNextLevel(executive);
// 요청 생성
Application app1 = new Application("휴가", 2, "회의 참석");
Application app2 = new Application("휴가", 4, "가족 방문");
Application app3 = new Application("급여 인상", 500, "성과 보상");
Application app4 = new Application("급여 인상", 1000, "특별 보상");
regular.process(app1);
regular.process(app2);
regular.process(app3);
regular.process(app4);
}
}