📝 Delegate Pattern(위임 패턴)
- (사전적 의미) 위임하다
- 객체가 자신의 기능을 다른 객체에게 위임하여 기능을 실행하는 디자인 패턴
- 객체 간의 결합도를 낮춰 유지보수성과 확장성을 높이는데 사용
- 사용자가 기능에 직접 접근하거나 기능을 직접 구현하지 않고, 대리자에게 위임하여 구현
📝 Strategy Pattern(전략 패턴)
- (사전적 의미) 전략
- 알고리즘 전략을 캡슐화하고 런타임에 선택할 수 있게 하는 행위 디자인 패턴
- 새로운 전략 추가 시 기존 코드 수정 없이 새로운 클래스만 추가하면 됨
- 조건문(if/switch)을 제거할 수 있음
조건문으로 구현하면 생기는 문제
public long calculate(String operation, long op1, long op2) {
if ("ADD".equals(operation) return op1 + op2;
else if ("SUB".equals(operation) return op1 - op2;
else if ("MUL".equals(operation) return op1 * op2;
else if ("DIV".equals(operation) return op1 / op2;
// MOD를 추가하려면 else if문을 또 추가해야 함
else throw new IllegalArgumentException("지원하지 않는 연산입니다.");
}
- 예시 자체는 정수 사칙연산이라는 매우 간단한 조건이기 때문에 조건문을 추가하는게 큰 부담으로 느껴지지 않을 수도 있지만… 상황에 따라 각 조건문이 매우 길어질 수도 있고, 근본적으로 기존 코드를 직접 수정해야 한다는 문제가 있다. (OCP - 개방 폐쇄 원칙을 위배)
🫱🏻🫲🏻 두 패턴을 함께 사용하는 이유
위임 패턴과 전략 패턴의 역할이 다르기 때문이다.
- Delegate(위임) 패턴 → 사용자(Service)와 실제 로직 사이의 결합도를 낮춤
- Strategy(전략) 패턴 → 여러 알고리즘 중 적절한 것을 선택
두 패턴을 함께 사용하면 사용자(Service)는 구체적인 연산 로직을 전혀 모르는 상태로 대리자에게 위임하고, 대리자는 전략 패턴으로 적절한 알고리즘을 선택해 실행한다.
이렇게 하면 사용자와 로직 간 결합도가 낮으면서 새로운 연산을 추가할 때 새로운 클래스 하나만 추가하면 된다.
⌨️ 코드 구현
제약사항
- 조건문(if/switch) 금지
- 아래 enum 클래스 사용
public enum CalculateOperation {
ADD,
SUB,
MUL,
DIV,
;
}
전체 구조
역할 클래스 설명
| 역할 | 클래스 | 설명 |
|---|---|---|
| 사용자 | CalculateService | 기능을 직접 구현하지 않고 대리자에게 위임 |
| 대리자 | Calculator | 적절한 전략을 선택해 실행 |
| 기능 | Add, Sub, Mul, Div | 연산 캡슐화 |
사용자가 기능에 직접 접근하거나 기능을 직접 구현하지 않고 대리자에게 위임하여 정수 사칙연산 기능을 사용하는 구조이다.
대리자는 기능에서 적절한 전략(연산)을 선택해 실제 계산을 수행하고 결과를 반환한다.
사용자(CalculateService)
@Service
@RequiredArgsConstructor
public class CalculateService implements CalculateUseCase {
private final Calculator calculator;
@Override
public long calculate(CalculateOperation operation, long op1, long op2) {
return calculator.calculate(operation, op1, op2); // 위임
}
}
// 인터페이스
public interface CalculateUseCase {
long calculate(CalculateOperation operation, long op1, long op2);
}
사용자는 정수 사칙연산 기능에 직접 접근하거나 기능을 직접 구현하지 않고, calculator라는 대리자에게 위임하여 정수 사칙연산 기능을 이용
대리자(Calculator)
@Component
public class Calculator {
private final Map<CalculateOperation, CalculateStrategy> strategyMap;
public Calculator(List<CalculateStrategy> strategies) {
this.strategyMap = new HashMap<>();
for (CalculateStrategy strategy : strategies) {
strategyMap.put(strategy.getOperator(), strategy);
}
}
public long calculate(CalculateOperation operation, long op1, long op2) {
// Map에서 적절한 전략 선택 후 실제 계산 수행
CalculateStrategy strategy = strategyMap.get(operation);
if (strategy == null) throw new IllegalArgumentException("지원하지 않는 연산입니다.");
return strategy.calculate(op1, op2);
}
}
대리자는 적절한 전략을 선택해 계산 결과를 반환
- Map 자료구조를 활용해 O(1)로 전략을 선택하고 계산 수행
- key enum
- value 기능 객체
기능(Add, Sub, Mul, Div)
@Component
class Add implements CalculateStrategy {
@Override
public long calculate(long op1, long op2) {
return op1 + op2;
}
@Override
public CalculateOperation getOperator() {
return CalculateOperation.ADD;
}
}
@Component
class Sub implements CalculateStrategy {
@Override
public long calculate(long op1, long op2) {
return op1 - op2;
}
@Override
public CalculateOperation getOperator() {
return CalculateOperation.SUB;
}
}
@Component
class Mul implements CalculateStrategy {
@Override
public long calculate(long op1, long op2) {
return op1 * op2;
}
@Override
public CalculateOperation getOperator() {
return CalculateOperation.MUL;
}
}
@Component
class Div implements CalculateStrategy {
@Override
public long calculate(long op1, long op2) {
if (op2 != 0) return op1 / op2;
throw new ArithmeticException("0으로 나눌 수 없습니다.");
}
@Override
public CalculateOperation getOperator() {
return CalculateOperation.DIV;
}
}
// 인터페이스
public interface CalculateStrategy {
long calculate(long op1, long op2);
CalculateOperation getOperator();
}
기능은 입력받은 두 가지 정수에 대해 각자의 역할을 수행
- MOD 연산이 추가된다면?
- enum에 MOD 추가
- Mod 전략 클래스를 하나 생성
@Component
class Mod implements CalculateStrategy {
@Override public long calculate(long op1, long op2) {
if (op2 != 0) return op1 % op2;
throw new ArithmeticException("0으로 나눌 수 없습니다.");
}
@Override
public CalculateOperation getOperator() {
return CalculateOperation.MOD;
}
}
기존 코드는 전혀 수정하지 않고도 새로운 연산을 추가할 수 있음
🔗 참고 자료
Delegate Pattern(델리게이트 패턴, 위임 패턴)이란?
* 23.03.29 코드수정 Delegate 패턴이 '헤드퍼스트 디자인 패턴' 도서에 나와있지 않아서 정리해보고자 한다. 아무래도 Decorator 패턴 또는 strategy 패턴과 비슷하기 때문일까? 또한, 이 Delegate 패턴이 Deco
ssdragon.tistory.com
💠 전략(Strategy) 패턴 - 완벽 마스터하기
Strategy Pattern 전략 패턴은 실행(런타임) 중에 알고리즘 전략을 선택하여 객체 동작을 실시간으로 바뀌도록 할 수 있게 하는 행위 디자인 패턴 이다. 여기서 '전략'이란 일종의 알고리즘이 될 수
inpa.tistory.com
<Spring> List, Map으로 빈 주입 받기
List, Map으로 빈 주입받기 List랑 Map으로 여러 개의 빈을 주입받을 수 있다. 아래 코드를 통해 어떻게 주입을 받는지 테스트하였다. 예제 우선 인터페이스 하나를 정의하고 하나의 인터페이스를
oniic.tistory.com
'𝑺𝒕𝒖𝒅𝒚 > 디자인 패턴' 카테고리의 다른 글
| [JAVA/Spring Boot] getter interface를 활용해 OCP를 준수한 SapMapper 구현하기 (0) | 2026.04.23 |
|---|---|
| [JAVA/Spring Boot] Facade 패턴으로 트랜잭션 타임아웃 처리 개선하기 (0) | 2026.03.31 |