[디자인패턴] 책임연쇄 패턴(Chain Of Responsibility)

Damsul·2023년 1월 16일
0

디자인패턴

목록 보기
6/15

메시지 송신측과 수신측을 분리하여 요청을 처리하는 기회를 다른 객체에 분산한다. 요청이 들어오면 해당 요청을 처리할 수 있는 객체를 찾을 때까지 연결 고리를 따라 요청을 전달하고, 적합한 객체를 발견하면 요청한 서비스를 제공하는 행위 패턴

  1. 메시지를 보내는 객체와 처리하는 객체를 분리하고 싶을때
  2. 하나의 메시지를 여러 객체가 처리해야할때
    사용한다.

출처 : 위키디피아

  • Handler : 요청을 처리하는 인터페이스를 정의하고, 다음 객체에게 메시지 전송
  • ConcreteHandler : 해당 요청을 처리할 수 있는 경우 직접 처리
  • Client : ConcreteHandler 객체에게 필요한 요청 전송

예제

ATM 기기에서 돈을 인출한다고 가정해보자. 책임연쇄 패턴을 이용하면
10만원, 5만원, 1만원 단위로 인출이 가능하다.
만약, 천원권 이하의 단위로 입력하면 다시 입력하게 한다.

  • Handler

[WithdrawChain.java]

public abstract class WithdrawChain {
    WithdrawChain withdrawChain;

    WithdrawChain setNext(WithdrawChain withdrawChain) {
        this.withdrawChain = withdrawChain;
        return withdrawChain;
    }

    abstract void withdraw(Currency currency);
}

[Currency.java] - Dto 객체

public class Currency {
    private int amount;
    public Currency(int money) {
        this.amount = money;
    }

    public int getAmount() {
        return amount;
    }

}
  • ConcreteHandler

[Withdraw100000Won.java]

public class Withdraw100000Won extends WithdrawChain {
    @Override
    public void withdraw(Currency currency) {
        if (currency.getAmount() % 10000 != 0) {
            System.out.println("10000원 단위로 다시 입력해 주세요");
            return;
        }

        if (currency.getAmount() >= 100000) {
            int cnt = currency.getAmount() / 100000;
            System.out.println("10만원짜리 " + cnt + "장이 인출 되었습니다.");
            int remainder = currency.getAmount() % 100000;
            if (remainder != 0) {
                this.withdrawChain.withdraw(new Currency(remainder));
            }
        } else {
            this.withdrawChain.withdraw(currency);
        }
    }
}

[Withdraw50000Won.java]

public class Withdraw50000Won extends WithdrawChain {
    @Override
    public void withdraw(Currency currency) {
        if (currency.getAmount() >= 50000) {
            int cnt = currency.getAmount() / 50000;
            System.out.println("5만원짜리 " + cnt + "장이 인출 되었습니다.");
            int remainder = currency.getAmount() % 50000;
            if (remainder != 0) {
                this.withdrawChain.withdraw(new Currency(remainder));
            }
        } else {
            this.withdrawChain.withdraw(currency);
        }
    }
}

[Withdraw10000Won.java]

public class Withdraw10000Won extends WithdrawChain {
    @Override
    public void withdraw(Currency currency) {
        if (currency.getAmount() >= 10000) {
            int cnt = currency.getAmount() / 10000;
            System.out.println("1만원짜리 " + cnt + "장이 인출 되었습니다.");
            int remainder = currency.getAmount() % 10000;
            if (remainder != 0) {
                this.withdrawChain.withdraw(new Currency(remainder));
            }
        } else {
            this.withdrawChain.withdraw(currency);
        }
    }
}
  • Client

[Client.java]

public class Client {
    public static void main(String[] args) throws IOException {
    // 체이닝 방식 1
        WithdrawChain withdraw100000Won = new Withdraw100000Won();
        withdraw100000Won.setNext(new Withdraw50000Won())
            .setNext(new Withdraw10000Won());
            
    // 체이닝 방식 2
//        Withdraw50000Won withdraw50000Won = new Withdraw50000Won();
//        Withdraw10000Won withdraw10000Won = new Withdraw10000Won();
//        withdraw100000Won.setNext(withdraw50000Won);
//        withdraw50000Won.setNext(withdraw10000Won);

        while (true) {
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("인출할 금액을 적어주세요.");
            int money = Integer.parseInt(br.readLine());
            if (money <= 0) {
                System.out.println("종료하겠습니다.");
                break;
            }
            Currency currency = new Currency(money);
            withdraw100000Won.withdraw(currency);
            System.out.println("--------------------------");
        }
    }
}
  • 실행 결과

장단점

  • 장점

    • 결합도를 낮추며, 요청의 발신자와 수신자를 분리시킬 수 있다.
    • 클라이언트는 처리객체의 집합 내부의 구조를 알 필요가 없다.
    • 집합 내의 처리 순서를 변경하거나 처리객체를 추가 또는 삭제할 수 있어 유연성이 향상된다.
    • 새로운 요청에 대한 처리객체 생성이 매우 편리해진다.
  • 단점

    • 충분한 디버깅을 거치지 않았을 경우 집합 내부에서 사이클이 발생할 수 있다.
    • 디버깅 및 테스트가 쉽지 않다.
profile
내 맘대로 작성하는 개발일지/ 작고 소중한 개발창고

0개의 댓글