[Design Pattern] 책임 연쇄 패턴

Chain Of Responsibility Pattern

책임 연쇄 패턴은 메세지를 보내는 객체와 이를 받아 처리하는 객체들 간의 결합도를 없애기 위한 패턴이다. 하나의 요청에 대한 처리가 반드시 한 객체에서만 되지 않고 여러 객체에게 그 처리 기회를 부여하는 동작을 수행한다.

활용성

  1. 하나 이상의 객체가 요청을 처리해야 하고 그 요청 처리자중 어떤 것이 선행자인지 모를 때 처리자가 자동으로 확정되어야 할때.
  2. 메세지를 받을 객체를 명시하지 않을 채 여러 객체 중 하나에게 처리를 요청하고 싶을 때
  3. 요청을 처리할 수 있는 객체 집합이 동적으로 정의되어야 할 때

책임 연쇄 패턴 사용에 따른 결과

객체간 행동적 결합도 저하

다른 객체가 어떻게 요청을 처리하는지 알 필요가 없어진다. 단지 요청을 보내는 객체는 이 메세지가 적절하게 처리될 것이라는 것만 확신하면 된다. 송신측과 수신측 모두 서로를 모르고 또 연결된 객체들조차도 그 연결 구조가 어떻게 되는지 모른다. 그렇기 때문에 객체들간의 상호 작용을 단순화시킬 수 있다.

객체의 책임할당의 유연성 부여

객체의 책임을 여러 객체로 분산시킬 수 있기 때문에 런타임에 객체 연결고리를 변경하거나 추가하여 책임을 변경하거나 확장할 수 있다.

메세지 수신이 보장되지는 않음

어떤 객체가 이 처리에 대한 수신을 담당한다는 것을 명시하지 않으면 요청이 처리된다는 보장은 없다. 또한 객체들 간의 연결 고리가 잘 정의되어 있지 않다면 요청은 처리되지 못한 채로 버려진다.

책임 연쇄

구조

책임 연쇄의 구조는 다음과 같다.

Chain Of Responsibility Pattern Diagram

책임 연쇄의 런타임 구조는 다음과 같다.

Chain Of Responsibility Pattern Runtime Diagram

C++ 구현

1
2
3
4
5
6
7
8
9
10
11
12
void ChainOfResponsibilityPattern::calculate()
{
CalculatorHandler* calculatorHandler = new PlusHandler();
calculatorHandler->setNext(new MinusHandler());
calculatorHandler->setNext(new MultiHandler());
calculatorHandler->setNext(new DivideHandler());

calculatorHandler->calculate(5, 5, PLUS);
calculatorHandler->calculate(5, 5, MINUS);
calculatorHandler->calculate(5, 5, MULTI);
calculatorHandler->calculate(5, 5, DIVIDE);
}
1
2
3
4
5
6
7
enum Operation
{
PLUS,
MINUS,
MULTI,
DIVIDE,
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class CalculatorHandler
{
protected:
CalculatorHandler* calculatorHandler = nullptr;
Operation op;

public:
CalculatorHandler(Operation op) : op(op) {}

void setNext(CalculatorHandler* calc)
{
if(calculatorHandler != nullptr)
calculatorHandler->setNext(calc);
else
calculatorHandler = calc;
}

virtual void calculate(int n1, int n2, Operation op) = 0;

};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
class PlusHandler : public CalculatorHandler
{
public:
PlusHandler() : CalculatorHandler(PLUS) {}

void calculate(int n1, int n2, Operation op) override
{
if(op == this->op)
std::cout<<n1<<" + "<<n2<<" = "<<n1+n2<<std::endl;
else if (this->calculatorHandler != nullptr)
calculatorHandler->calculate(n1, n2, op);
}
};

class MinusHandler : public CalculatorHandler
{
public:
MinusHandler() : CalculatorHandler(MINUS) {}

void calculate(int n1, int n2, Operation op) override
{
if(op == this->op)
std::cout<<n1<<" - "<<n2<<" = "<<n1-n2<<std::endl;
else if (this->calculatorHandler != nullptr)
calculatorHandler->calculate(n1, n2, op);
}
};

class MultiHandler : public CalculatorHandler
{
public:
MultiHandler() : CalculatorHandler(MULTI) {}

void calculate(int n1, int n2, Operation op) override
{
if(op == this->op)
std::cout<<n1<<" * "<<n2<<" = "<<n1*n2<<std::endl;
else if (this->calculatorHandler != nullptr)
calculatorHandler->calculate(n1, n2, op);
}
};

class DivideHandler : public CalculatorHandler
{
public:
DivideHandler() : CalculatorHandler(DIVIDE) {}

void calculate(int n1, int n2, Operation op) override
{
if(op == this->op)
std::cout<<n1<<" / "<<n2<<" = "<<n1/n2<<std::endl;
else if (this->calculatorHandler != nullptr)
calculatorHandler->calculate(n1, n2, op);
}
};

java 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Client {
public static void main(String[] args) {
CalculatorHandler calculatorHandler = new PlusHandler();
calculatorHandler.setNext(new MinusHandler());
calculatorHandler.setNext(new MultiHandler());
calculatorHandler.setNext(new DivideHandler());

calculatorHandler.calculate(5, 5, Operation.PLUS);
calculatorHandler.calculate(5, 5, Operation.MINUS);
calculatorHandler.calculate(5, 5, Operation.MULTI);
calculatorHandler.calculate(5, 5, Operation.DIVIDE);
}
}
1
2
3
4
5
6
public enum Operation {
PLUS,
MINUS,
MULTI,
DIVIDE
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public abstract class CalculatorHandler {
protected CalculatorHandler calculatorHandler;
protected Operation op;

public CalculatorHandler(Operation op) {
this.op = op;
}

void setNext(CalculatorHandler calc) {
if(Objects.isNull(calculatorHandler))
calculatorHandler = calc;
else
calculatorHandler.setNext(calc);
}

abstract void calculate(int n1, int n2, Operation op);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class PlusHandler extends CalculatorHandler{
public PlusHandler() {
super(Operation.PLUS);
}

@Override
void calculate(int n1, int n2, Operation op) {
if(op.equals(this.op))
System.out.printf("%d + %d = %d\n",n1, n2, n1 + n2);
else if(!Objects.isNull(calculatorHandler))
calculatorHandler.calculate(n1, n2, op);
}
}

public class MinusHandler extends CalculatorHandler{
public MinusHandler() {
super(Operation.MINUS);
}

@Override
void calculate(int n1, int n2, Operation op) {
if(op.equals(this.op))
System.out.printf("%d - %d = %d\n",n1, n2, n1 - n2);
else if(!Objects.isNull(calculatorHandler))
calculatorHandler.calculate(n1, n2, op);
}
}

public class MultiHandler extends CalculatorHandler{
public MultiHandler() {
super(Operation.MULTI);
}

@Override
void calculate(int n1, int n2, Operation op) {
if(op.equals(this.op))
System.out.printf("%d * %d = %d\n",n1, n2, n1 * n2);
else if(!Objects.isNull(calculatorHandler))
calculatorHandler.calculate(n1, n2, op);
}
}

public class DivideHandler extends CalculatorHandler{
public DivideHandler() {
super(Operation.DIVIDE);
}

@Override
void calculate(int n1, int n2, Operation op) {
if(op.equals(this.op))
System.out.printf("%d / %d = %d\n",n1, n2, n1 / n2);
else if(!Objects.isNull(calculatorHandler))
calculatorHandler.calculate(n1, n2, op);
}
}
Author: Song Hayoung
Link: https://songhayoung.github.io/2020/08/21/Design%20Pattern/ChainOfResponsibilityPattern/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.