-
책임 연쇄 패턴이란?Java 2022. 7. 19. 00:01
책임 연쇄 패턴(Chain of responsibility)이란?
책임이란?
클래스 단위로 무언가를 처리하는 기능
연쇄란?
여러 개의 책임들을 동적으로 연결해서 순차적으로 실행함
책임 연쇄란?
즉, 기능을 클래스 별로 분리한 후 각 기능(클래스)을 동적으로 연결하여 순차적으로 실행합니다.
다음은 책임 연쇄 패턴의 예시입니다.
여러개의 클래스들이 클라이언트의 요청을 처리하기 위해 객체를 체인 형태로 전달하여 결합력을 낮춥니다.
등장배경
이해를 하기 쉽게 여러가지 방법을 사용해 결제하는 상황을 연출하여 보겠습니다.
만약 결제를 할 때 다음과 같은 수단으로 결제할 수 있다고 가정하겠습니다.
- 현금(Cash)으로 결제할 수 있다.
- 신용카드(Credit Card)로 결제할 수 있다.
간단하게 코드를 작성하여보면 if-else를 활용하여 위의 그림처럼 작성할 수 있습니다.
만약 Debit Card로 결제할 수 있는 수단을 추가해야 한다면?
else if문을 추가해서 코드를 작성해야 합니다.
이때 책임 연쇄 패턴을 사용하면 각각을 모듈으로 변환하고 이를 연결합니다.
1번 시나리오(Cash로 지불할 때)
첫번째로 CashHandler를 만납니다.
현재 Cash로 지불하는 상황이므로 CashHandler에 의해 결제됩니다.
2번 시나리오(DebitCard로 지불할 때)
첫번째로 CashHandler를 만납니다. (요청을 처리할 수 없어서 다음 Handler를 호출합니다)
두번째로 CreditCardHandler를 만납니다. (요청을 처리할 수 없어서 다음 Handler를 호출합니다)
세번째로 DebitCardHandler를 만납니다. (요청을 처리할 수 있기 때문에 요청을 처리합니다)
여기에 만약 Paypal으로 결제하는 PaypalHandler를 추가하게 된다면?
PaypalHandler라는 모듈을 만들고 연결만 지어주면 됩니다.
책임 연쇄 패턴의 장점
- 사슬에 들어가는 객체를 바꾸거나 순서를 바꿈으로써 역할을 동적으로 추가하거나 제거할 수 있습니다.
- 객체는 사슬의 구조를 몰라도 됩니다.
책임 연쇄 패턴의 단점
- 디버깅 하기 힘들다
- 여러 클래스를 구현해야 한다.
- 요청이 반드시 수행된다는 보장이 없습니다 (단점이자 장점으로 작용할 수도 있습니다)
책임 연쇄 패턴이 사용되는 예시
javax.servlet.Filter#doFilter()가 책임연쇄패턴을 사용하고 있습니다.
책임 연쇄 패턴의 구현
ATM기기에서 10만원, 5만원, 만원 단위로 인출하는 과정을 구현해 보겠습니다.
만약 사용자가 천원 단위를 입력하면 오류가 발생합니다.
인출할 돈을 저장하는 클래스 Currency 클래스를 만들고 체인으로 사용할 WithdrawChain 인터페이스를 만들겠습니다.
Currency 클래스
public class Currency { private int amount; public int getAmount() { return amount; } public Currency(int amount) { this.amount = amount; } }
WithdrawChain 인터페이스
public interface WithdrawChain { void setNextChain(WithdrawChain withdrawChain); void withdraw(Currency currency); }
인터페이스는 다음 처리를 지정해주는 setNextChain()과 요청을 처리할 withdraw 메서드로 구성됩니다.
이제 각각 10만원, 5만원권, 만원권을 인출할 수 있도록 3개의 구현클래스를 만들겠습니다.
Withdraw100000Won.java
//십만원 public class Withdraw100000Won implements WithdrawChain { //Withdraw50000Won 체인 연결 private WithdrawChain withdrawChain; @Override public void setNextChain(WithdrawChain nextChain) { this.withdrawChain = nextChain; } @Override public void withdraw(Currency currency) { //10만원 이상일 경우에만 인출가능 if(currency.getAmount() >= 100000) { int num = currency.getAmount()/100000; int remain = currency.getAmount() % 100000; System.out.println("10만원짜리 " +num+"장이 인출되었습니다."); if(remain > 0) { this.withdrawChain.withdraw(new Currency(remain)); } //남은 돈이 없으면 더이상 체이닝이 의미가 없음. return; } //10만원 미만일 경우에는 다음 체이닝 호출 this.withdrawChain.withdraw(currency); } }
Withdraw50000Won.java
//오만원 public class Withdraw50000Won implements WithdrawChain { //Withdraw10000Won 체인 연결 private WithdrawChain withdrawChain; @Override public void setNextChain(WithdrawChain nextChain) { this.withdrawChain = nextChain; } @Override public void withdraw(Currency currency) { //5만원 이상일 경우에만 인출가능 if(currency.getAmount() >= 50000) { int num = currency.getAmount()/50000; int remain = currency.getAmount() % 50000; System.out.println("5만원짜리 " +num+"장이 인출되었습니다."); if(remain > 0) { this.withdrawChain.withdraw(new Currency(remain)); } //남은 돈이 없으면 더이상 체이닝이 의미가 없음. return; } //5만원 미만일 경우에는 다음 체이닝 호출 this.withdrawChain.withdraw(currency); } }
Withdraw10000Won.java
//만원 public class Withdraw50000Won implements WithdrawChain { private WithdrawChain withdrawChain; @Override public void setNextChain(WithdrawChain nextChain) { this.withdrawChain = nextChain; } @Override public void withdraw(Currency currency) { //1만원 이상일 경우에만 인출가능 if(currency.getAmount() >= 10000) { int num = currency.getAmount()/10000; int remain = currency.getAmount() % 10000; System.out.println("1만원짜리 " +num+"장이 인출되었습니다."); if(remain > 0) { this.withdrawChain.withdraw(new Currency(remain)); } //남은 돈이 없으면 더이상 체이닝이 의미가 없음. return; } //1만원 미만일 경우에는 다음 체이닝 호출 this.withdrawChain.withdraw(currency); } }
만약 처리기가 아무것도 처리를 하지 못해도 그 요청 전부를 다음 체인으로 넘깁니다.
구현 클래스를 통해 체인 만들기
만든 구현클래스를 통해 체인을 만드는 과정입니다.
조심해야 할 점은 처리기 순서를 만원 -> 5만원 -> 10만원으로 하게 되면 모든 돈이 만원 권으로 나오기 때문에 두번째 처리기부터는 사용하지 않아 불필요합니다.
ChainOfResponsibilityTests.java
public class ChainOfResponsibilityTests { public static void main(String[] args) { WithdrawChain withdraw100000Won = new Withdraw100000Won(); WithdrawChain withdraw50000Won = new Withdraw50000Won(); WithdrawChain withdraw10000Won = new Withdraw10000Won(); withdraw100000Won.setNextChain(withdraw50000Won); withdraw50000Won.setNextChain(withdraw10000Won); while(true) { System.out.println("인출 할 돈을 눌러주세요"); Scanner scanner = new Scanner(System.in); int money = scanner.nextInt(); Currency currency = new Currency(money); withdraw100000Won.withdraw(currency); System.out.println("--------------"); } } }
결과
인출 할 돈을 눌러주세요 50000 5만원짜리 1장이 인출되었습니다. -------------- 인출 할 돈을 눌러주세요 130000 10만원짜리 1장이 인출되었습니다. 1만원짜리 3장이 인출되었습니다. -------------- 인출 할 돈을 눌러주세요 70000 5만원짜리 1장이 인출되었습니다. 1만원짜리 2장이 인출되었습니다. --------------
UML(Unified Modeling Language)
출처
https://www.youtube.com/watch?v=FAHEWQD6EVE
https://www.youtube.com/watch?v=Y0xn5ihjhg4
https://donghyeon.dev/design%20pattern/2020/05/11/Chain-of-Responsibility-%ED%8C%A8%ED%84%B4/
https://joanne.tistory.com/180
'Java' 카테고리의 다른 글
[Java]ExecutorService란? (0) 2022.07.31 Java Lazy Evaluation이란? (0) 2022.07.24 불변객체란 무엇인가? (0) 2022.05.29 Java에 원시타입(primitive type)이 존재하는 이유 (0) 2022.05.28 전략 패턴이란? (0) 2022.05.25