| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
Tags
- 코딩테스트
- 시스템호출
- 파이썬
- Spring
- @Component
- C언어
- OpenCV
- Nodejs
- @Autowired
- 카카오
- 프로그래머스
- git
- C++
- 스프링
- 가상면접사례로배우는대규모시스템설계기초
- 코테
- thymeleaf
- python
- 알고리즘
- 카카오 코테
- spring boot
- 카카오 알고리즘
- nestJS
- TypeORM
- 구조체배열
- nestjs typeorm
- 해시
- nestjs auth
- AWS
- 컴포넌트스캔
Archives
- Today
- Total
공부 기록장 💻
[디자인패턴] 전략 패턴 (Strategy Pattern) 에 대해 알아보자 본문
전략 패턴(Strategy Pattern) 이란?
전략 패턴이란, 실행 중에 알고리즘을 선택할 수 있게 하는 행위 소프트웨어 디자인 패턴 (Behavior Design Pattern) 이다. 특정한 계열의 알고리즘들을 정의하고, 각 알고리즘을 캡슐화 하여 이 알고리즘들을 해당 계열 안에서 상호 교체가 가능하게 만든다.
각 객체들이 할 수 있는 행위(알고리즘) 각각에 대해 전략 클래스를 생성하고, 유사한 행위들을 캡슐화하는 인터페이스를 정의하여, 객체의 행위를 동적으로 바꾸고 싶은 경우 직접 행위를 수정하지 않고 전략을 바꿔줌으로써 행위를 유연하게 확장하는 방법을 말한다.
여기서 포인트는?
- 유사한 행위에 대한 알고리즘을 각각 정의하여 캡슐화 한다.
- 객체의 행위(알고리즘, 전략)를 동적으로 바꿀 수 있는데, 직접 수정하는 것이 아니라 주입을 통해 유연하게 확장한다.

전략 패턴을 구성하는 3가지 요소
- Client : 전략 객체를 생성하여 컨텍스트에 주입하는 제 3자 (공급자)
- 클라이언트는 다양한 전략 중 하나를 선택하여 생성한 후, 컨텍스트에게 주입한다.
- Context : 전략 객체를 사용하는 소비자
- 공통되는 로직이 작성되어 있는 클래스
- Strategy : 구체적인 전략의 공통, 추상체
- Strategy 인터페이스와 각 비즈니스 로직을 담당하는 하위 구현체들을 선언
- ConcreteStrategy : 변경되는 구체적인 전략 구현체
- 새로운 비즈니스 로직이 추가되면, 인터페이스나 다른 구현체 변경 없이 새로 추가하기만 하면 됨
전략 패턴의 개념도 (스프링 입문을 위한 자바 객체 지향의 원리와 이해)

길찾기 예시를 통해 알아보자!
- 여러 이동 수단을 이용하는 길찾기 최적 경로 프로그램예시 ) 네이버 길찾기

- 사용자로부터 출발지, 도착지, 그리고 이동 수단을 입력 받아 길찾기를 진행하고 최적 경로를 알려주는 프로그램
- 기존 코드 if-else 분기 구문으로 이루어진 기존 코드의 단점
- 확장의 어려움
- 새로운 수단 (ex) 자전거, 미래 모빌리티) 을 통한 길찾기를 하고 싶은 경우, 코드가 길어지고 복잡해진다.
System.out.println("이동 수단을 선택해 주세요.(0. 종료 1. 대중 교통 2. 자동차 3. 도보 4. 자전거 5. 미래 모빌리티)");else if (userInput==4){ System.out.println("자전거를 이용한 최적 경로를 계산합니다."); // } else if (userInput==5){ System.out.println("미래 모빌리티 XXX를 이용한 최적 경로를 계산합니다."); // }- 누락, 변경의 위험성
- 기존 코드를 수정하는 과정에서 수정하는 위치를 매번 찾아야 하고, 수정하면서 다른 코드에 영향을 미칠 수 있다.
- 확장의 어려움
public class Main { public static void main(String[] args) { Scanner input = new Scanner(System.in); System.out.println("출발 지점을 입력해 주세요."); String departure = input.next(); System.out.println("도착 지점을 입력해 주세요."); String destination = input.next(); System.out.println("이동 수단을 선택해 주세요.(0. 종료 1. 대중 교통 2. 자동차 3. 도보)"); int userInput = input.nextInt(); if (userInput==1){ System.out.println("대중 교통을 이용한 최적 경로를 계산합니다."); // 대중 교통 기반 최적 경로 계산 } else if (userInput==2){ System.out.println("자동차를 이용한 최적 경로를 계산합니다."); // 자동차를 이용한 최적 경로 계산 } else if (userInput==3){ System.out.println("도보를 이용한 최적 경로를 계산합니다."); // 도보를 이용한 최적 경로 계산 } } }- 기존의 분기 코드
-
- 전략 Strategy 코드
- trace() 메서드: 출발지에서 도착지까지의 최적 경로를 계산하는 추상 메소드
전략 구현체 : PublicTransportStrategy전략 구현체 : CarStrategy전략 구현체 : WalkStrategy전략 구현체 : BicycleStrategy새로운 전략 구현체 : FutureMobilityXXX ?→ OCP(Open-Closed Principle, 개방 폐쇄 원칙) : 확장에 대해서는 열려 있고, 변경에 대해서는 닫혀 있는 원칙public interface RouteStrategy { void trace(LocationInput locationInput); }
- public class BicycleStrategy implements RouteStrategy { @Override public void trace(LocationInput locationInput) { System.out.println(locationInput.getDeparture() + "에서 "+locationInput.getDestination()+"까지 자전거를 이용한 최적 경로 입니다.\\n\\n"); // } }
- public class WalkingStrategy implements RouteStrategy { @Override public void trace(LocationInput locationInput) { System.out.println(locationInput.getDeparture() + "에서 "+locationInput.getDestination()+"까지 도보를 이용한 최적 경로입니다.\\n\\n"); // } }
- public class CarStrategy implements RouteStrategy { @Override public void trace(LocationInput locationInput) { System.out.println(locationInput.getDeparture() + "에서 "+locationInput.getDestination()+"까지 자동차를 이용한 최적 경로 입니다.\\n\\n"); // } }
- public class PublicTransportStrategy implements RouteStrategy { @Override public void trace(LocationInput locationInput) { System.out.println(locationInput.getDeparture() + "에서 "+locationInput.getDestination()+"까지 대중 교통을 이용한 최적 경로 입니다.\\n\\n"); // } }
- 전략 : RouteStrategy 인터페이스
- Context 코드
- execute(): 주입받은 RouteStrategy 구현체의 trace() 메서드를 호출
→ DIP (Dependency Injection Principle, 의존성 주입 원칙) : 상위 모듈이 하위 모듈에 의존하지 않음public class ShortestPathCalculator { private LocationInput locationInput; private RouteStrategy routeStrategy; void setLocation(LocationInput locationInput){ this.locationInput = locationInput; } void setRouteStrategy(RouteStrategy strategy){ this.routeStrategy = strategy; } void execute(){ System.out.println("출발 : " + locationInput.getDeparture() + "\\n도착 : " + locationInput.getDestination()); routeStrategy.trace(locationInput); } } - 컨텍스트 : ShortestPathCalculator
- Client 코드
- 전략 객체를 생성하고 주입하는 공급자
public class MapUserClient { public static void main(String[] args) { Scanner input = new Scanner(System.in); ShortestPathCalculator pathCalculator = new ShortestPathCalculator(); LocationInput locationInput = new LocationInput(); System.out.println("출발 지점을 입력해 주세요."); locationInput.setDeparture(input.next()); System.out.println("도착 지점을 입력해 주세요."); locationInput.setDestination(input.next()); pathCalculator.setLocation(locationInput); while(ifInput){ System.out.println("이동 수단을 선택해 주세요.(1. 대중 교통 2. 자동차 3. 도보 4. 자전거)"); int userInput = input.nextInt(); switch (userInput) { case PUBLIC_TRANSPORTATION: pathCalculator.setRouteStrategy(new PublicTransportStrategy()); continue; case AUTO_MOBILE: pathCalculator.setRouteStrategy(new CarStrategy()); continue; case WALK: pathCalculator.setRouteStrategy(new WalkingStrategy()); continue; case BICYCLE: pathCalculator.setRouteStrategy(new BicycleStrategy()); } pathCalculator.execute(); // 실행 } } }public class LocationInput { private String departure; private String destination; void setDeparture(String departure){ this.departure = departure; } void setDestination(String destination){ this.destination = destination; } String getDeparture(){return departure;} String getDestination(){return destination;} }public class ClientCommand { public static final int EXIT = 0; public static final int PUBLIC_TRANSPORT = 1; public static final int CAR = 2; public static final int WALK = 3; public static final int BICYCLE = 4; } - Client : MapUserClient전략 패턴을 적용한 새로운 코드
- 전략 Strategy 코드

전략 패턴은 언제 적용하는 것이 좋을까?
- 객체 내에서 다른 알고리즘을 사용하고, 런타임 중에 한 알고리즘에서 다른 알고리즘으로 전환이 필요할 때
- 일부 동작을 실행하는 방식만 다른 유사한 클래스가 많이 있는 경우
- 패턴을 사용하여, 컨텍스트에서 중요하지 않을 수 있는 알고리즘을 세부 구상체에서 구현하여, 클래스의 비즈니스 로직을 분리하고자 할 때
- 클래스에 동일한 계열 내의 서로 다른 알고리즘을 전환하는 거대한 덩어리의 조건문이 있는 경우
🧐 전략 패턴의 장점
- 새로운 전략을 추가해도 기존 코드(Context 코드)를 변경하지 않는다. (OCP)
- 상속 대신 위임을 사용할 수 있다. (DIP)
- 실행 중 동적으로 전략의 변경이 가능하다. (런타임 시 전략 변경)
- 알고리즘의 구현 세부 정보를 분리할 수 있다.
☹️ 전략 패턴의 단점
- 클라이언트 코드가 구체적인 전략을 알아야 한다.
- 전략 패턴 도입으로 설계의 복잡도가 증가한다.
- 한 두 가지의 알고리즘만 있고, 패턴이 거의 변경되지 않는 경우 과도하게 추상화하여 복잡하게 만들 필요가 없다.
- 최신 모던 프로그래밍 기술을 이용해 익명 함수를 활용하여 다양한 버전의 알고리즘을 구현할 수 있기 때문에, 인터페이스와 클래스로 분리하여 코드를 부풀리지 않는 것이 좋다.
전략 패턴을 한 문장으로 요약해본다면?
클라이언트의 전략을 생성해 전략을 실행할 컨텍스트에 주입하는 패턴!
728x90
반응형
'# Tech Studies' 카테고리의 다른 글
| [Ubuntu/Nodejs] Ubuntu 에서 apt를 이용해 Node.js 설치하기 (0) | 2023.02.13 |
|---|
Comments