ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 책임 연쇄 패턴이란?
    Java 2022. 7. 19. 00:01

    책임 연쇄 패턴(Chain of responsibility)이란?

    책임이란?

    클래스 단위로 무언가를 처리하는 기능

     

    연쇄란?

    여러 개의 책임들을 동적으로 연결해서 순차적으로 실행함

     

    책임 연쇄란?

    즉, 기능을 클래스 별로 분리한 후 각 기능(클래스)을 동적으로 연결하여 순차적으로 실행합니다.

     

    다음은 책임 연쇄 패턴의 예시입니다.

    책임 연쇄 패턴 예시(https://donghyeon.dev/design%20pattern/2020/05/11/Chain-of-Responsibility-%ED%8C%A8%ED%84%B4/)

    여러개의 클래스들이 클라이언트의 요청을 처리하기 위해 객체를 체인 형태로 전달하여 결합력을 낮춥니다.

     

    등장배경

     

    이해를 하기 쉽게 여러가지 방법을 사용해 결제하는 상황을 연출하여 보겠습니다.

    https://www.youtube.com/watch?v=Y0xn5ihjhg4

     

    만약 결제를 할 때 다음과 같은 수단으로 결제할 수 있다고 가정하겠습니다.

    - 현금(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://donghyeon.dev/design%20pattern/2020/05/11/Chain-of-Responsibility-%ED%8C%A8%ED%84%B4/

     

     

     

    출처

    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/

     

    책임 연쇄 패턴

    Chain Of Responsibility(책임 연쇄 패턴)

    donghyeon.dev

    https://joanne.tistory.com/180

     

    책임연쇄패턴으로 분기문 처리하기

    책임 연쇄 패턴이란 명령 객체와 일련의 처리 객체를 포함하는 디자인 패턴이다. 각각의 처리 객체는 명령 객체를 처리할 수 있는 연산의 집합으로 구성되고, 체인 안의 처리 객체가 다룰 수 없

    joanne.tistory.com

     

    '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

    댓글

Designed by Tistory.