F-LAB JAVA · 5주차 · Phase 6 · 객체지향 설계 원칙 (OCP & 전략 패턴)
🏆 Phase 6 완주 — 지금까지의 진화를 패턴으로 명명
이 Unit을 끝내면 다음을 답할 수 있어야 한다.
전략 패턴은 변경되는 알고리즘을 인터페이스로 분리하고 구체 구현을 외부에서 주입받는 패턴으로, ShipmentDao (Context) 가 ConnectionMaker (Strategy) 인터페이스에 의존하고 N/DConnectionMaker (ConcreteStrategy) 를 주입받는 것이 바로 이 패턴이다.
전략 패턴의 구성 요소는 Context (전략을 사용하는 주체), Strategy (전략 인터페이스), ConcreteStrategy (구체 전략 구현) 다.
우리가 Phase 6 에서 만든 구조에서 ShipmentDao 가 Context, ConnectionMaker 가 Strategy, N/DConnectionMaker 가 ConcreteStrategy 에 정확히 대응한다.
전략은 외부에서 생성자 등으로 주입 되며, 이로써 새 전략 (ConcreteStrategy) 을 추가하면 Context 코드 변경 없이 알고리즘을 교체할 수 있어 전략 패턴이 곧 OCP 의 구현 도구 가 된다.
전략 패턴은 합성 (전략 주입) 기반이라는 점에서 상속 기반의 템플릿 메소드 패턴과 구별되며, Spring 곳곳 (Comparator,Resource,PlatformTransactionManager등) 에서 활용된다.
전략 패턴 = 내비게이션 경로 선택:
Context (내비게이션 앱):
- 길 안내 (전략 사용)
- "경로 계산해줘"
Strategy (경로 알고리즘 인터페이스):
- calculateRoute()
- 계약만
ConcreteStrategy (구체 경로):
- 최단 거리 전략
- 최소 시간 전략
- 무료 도로 전략
주입:
- 사용자가 전략 선택
- 앱에 주입
- 앱은 어떤 전략인지 모름 (인터페이스만)
OCP:
- 새 전략 (자전거 경로) 추가
- 앱 코드 변경 X
- 전략만 추가 (확장)
ShipmentDao = 내비 앱 (Context)
ConnectionMaker = 경로 알고리즘 (Strategy)
N/DConnectionMaker = 구체 경로 (ConcreteStrategy)
→ 전략 패턴 = 알고리즘 인터페이스 분리 + 주입, Context/Strategy/ConcreteStrategy.
1. 전략 패턴의 정의
2. 세 가지 구성 요소
3. ShipmentDao와 ConnectionMaker 매핑
4. 전략 주입
5. 전략 패턴 = OCP 구현
6. 전략 vs 템플릿 메소드
7. Spring의 전략 패턴
8. Phase 6 완주 정리
9. 면접 + 자기 점검
전략 패턴 (Strategy Pattern):
변경되는 알고리즘을 인터페이스로 분리,
구체 구현을 외부에서 주입.
- 알고리즘 = 전략
- 인터페이스로 추상화
- 런타임 교체
GoF 디자인 패턴:
행위 패턴 (Behavioral):
- 알고리즘군 정의
- 캡슐화
- 교체 가능
"알고리즘을 정의하고
캡슐화하여 교체 가능하게"
의도:
- 알고리즘 캡슐화
- 런타임 선택
- 조건 분기 제거
- 확장 (새 전략)
// 전략 패턴 — 운임 계산
public interface FreightStrategy { // Strategy
BigDecimal calculate(Shipment shipment);
}
public class ShipmentService { // Context
private final FreightStrategy freightStrategy;
public ShipmentService(FreightStrategy strategy) {
this.freightStrategy = strategy; // 전략 주입
}
public BigDecimal getFreight(Shipment s) {
return freightStrategy.calculate(s); // 전략 사용
}
}
// ConcreteStrategy 들
class SeaFreightStrategy implements FreightStrategy {
public BigDecimal calculate(Shipment s) {
return s.getWeight().multiply(BigDecimal.valueOf(10));
}
}
class AirFreightStrategy implements FreightStrategy {
public BigDecimal calculate(Shipment s) {
return s.getWeight().multiply(BigDecimal.valueOf(50));
}
}
전략 패턴 (Strategy Pattern) 의 정의는?
답:
1. 정의:
GoF:
의도:
요소:
전략 패턴 3요소:
1. Context
- 전략 사용 주체
2. Strategy (인터페이스)
- 전략 계약
3. ConcreteStrategy
- 구체 전략 구현
Context:
- 전략 보유 (합성)
- 전략 호출
- 구체 전략 모름
ShipmentDao
Strategy (인터페이스):
- 알고리즘 계약
- 추상화
ConnectionMaker
ConcreteStrategy:
- 구체 알고리즘
- Strategy 구현
N/DConnectionMaker
구조:
[Context]
- strategy (Strategy)
- execute() { strategy.algorithm(); }
↓ 사용
[Strategy] (인터페이스)
- algorithm()
↑ 구현
[ConcreteStrategyA] [ConcreteStrategyB]
// 3요소 (ILIC)
// Strategy (인터페이스)
public interface ConnectionMaker {
Connection makeConnection() throws Exception;
}
// Context
public class ShipmentDao {
private final ConnectionMaker connectionMaker; // 전략 보유
public ShipmentDao(ConnectionMaker connectionMaker) {
this.connectionMaker = connectionMaker;
}
public void add(Shipment s) throws Exception {
Connection c = connectionMaker.makeConnection(); // 전략 사용
}
}
// ConcreteStrategy 들
public class NConnectionMaker implements ConnectionMaker {
public Connection makeConnection() throws Exception { return null; }
}
public class DConnectionMaker implements ConnectionMaker {
public Connection makeConnection() throws Exception { return null; }
}
Context / Strategy / ConcreteStrategy 구성 요소는?
답:
1. Context:
Strategy:
ConcreteStrategy:
구조:
| 전략 패턴 | ILIC |
|---|---|
| Context | ShipmentDao |
| Strategy | ConnectionMaker |
| ConcreteStrategy | N/DConnectionMaker |
| algorithm() | makeConnection() |
Context = ShipmentDao:
- ConnectionMaker 보유
- makeConnection 호출
- 구체 모름
→ 전략 사용 주체
Strategy = ConnectionMaker:
- 연결 알고리즘 계약
- makeConnection()
- 인터페이스
→ 전략 추상화
우리가 만든 것:
Phase 6.1 에서:
- 인터페이스 + 주입
사실은:
- 전략 패턴
→ 패턴 이름 부여
// Phase 6.1~6.2 의 코드 = 전략 패턴
// Strategy (Phase 6.1 의 ConnectionMaker)
public interface ConnectionMaker {
Connection makeConnection() throws Exception; // algorithm()
}
// Context (Phase 6.1 의 ShipmentDao)
public class ShipmentDao {
private final ConnectionMaker connectionMaker; // 전략 보유
public ShipmentDao(ConnectionMaker cm) { this.connectionMaker = cm; }
public void add(Shipment s) throws Exception {
Connection c = connectionMaker.makeConnection(); // 전략 사용
}
}
// ConcreteStrategy (Phase 6.1 의 구현들)
public class CustomerAConnectionMaker implements ConnectionMaker {
public Connection makeConnection() throws Exception { return null; }
}
// → 우리가 만든 게 정확히 전략 패턴
ShipmentDao와 ConnectionMaker의 매핑은?
답:
1. Context:
Strategy:
ConcreteStrategy:
우리가 만든 것:
전략 주입:
생성자 주입:
- 생성 시 전략 받음
Setter 주입:
- 나중에 변경
→ 외부에서 전략 결정
// 생성자 주입
public class ShipmentDao {
private final ConnectionMaker connectionMaker;
public ShipmentDao(ConnectionMaker connectionMaker) {
this.connectionMaker = connectionMaker; // 주입
}
}
// 사용
ShipmentDao dao = new ShipmentDao(new NConnectionMaker());
// Setter 주입 (런타임 변경 가능)
public class ShipmentDao {
private ConnectionMaker connectionMaker;
public void setConnectionMaker(ConnectionMaker cm) {
this.connectionMaker = cm; // 변경 가능
}
}
// 런타임 전략 변경
dao.setConnectionMaker(new DConnectionMaker());
// 런타임 전략 선택
ConnectionMaker strategy = switch (config.getDbType()) {
case "mysql" -> new MySqlConnectionMaker();
case "oracle" -> new OracleConnectionMaker();
default -> throw new IllegalArgumentException();
};
ShipmentDao dao = new ShipmentDao(strategy);
// 설정에 따라 런타임 결정
// 전략 주입 (ILIC)
public class ShipmentDaoConfig {
public ShipmentDao createDao(String customerType) {
// 고객사별 전략 선택 (런타임)
ConnectionMaker strategy = switch (customerType) {
case "A" -> new CustomerAConnectionMaker();
case "B" -> new CustomerBConnectionMaker();
default -> throw new IllegalArgumentException();
};
// 전략 주입
return new ShipmentDao(strategy);
// 이 분기조차 IoC 컨테이너가 대신 (Phase 8)
}
}
// 전략 결정/주입을 외부가 담당
전략을 외부에서 주입받는 방식은?
답:
1. 주입:
생성자:
Setter:
선택:
전략 패턴 = OCP 구현:
새 전략 (ConcreteStrategy):
- 추가 (확장 ✅)
Context:
- 변경 X (닫힘 ✅)
→ OCP 달성
// 새 전략 추가 (확장)
public class PostgreSqlConnectionMaker implements ConnectionMaker {
public Connection makeConnection() throws Exception {
return null; // 새 전략
}
}
// Context (ShipmentDao) 변경 X
분기 제거:
전 (OCP 위반):
if (type) { ... } else if ...
후 (전략 패턴):
strategy.algorithm()
- 분기 X
- 다형성
전략 vs 분기:
분기 (if-else):
- 새 케이스 = 수정
- OCP 위반
전략 패턴:
- 새 전략 = 추가
- OCP 준수
// 전략 패턴으로 OCP
// ❌ 분기 (OCP 위반)
public BigDecimal calculateFreight(Shipment s, String mode) {
if (mode.equals("SEA")) return seaCalc(s);
else if (mode.equals("AIR")) return airCalc(s);
// 새 운송 → 수정
throw new IllegalArgumentException();
}
// ✓ 전략 패턴 (OCP)
interface FreightStrategy {
BigDecimal calculate(Shipment s);
}
class ShipmentService {
private final FreightStrategy strategy;
ShipmentService(FreightStrategy s) { this.strategy = s; }
BigDecimal getFreight(Shipment s) {
return strategy.calculate(s); // 분기 X
}
}
// 새 운송 = 새 FreightStrategy 구현 (확장, Service 변경 X)
class SeaFreightStrategy implements FreightStrategy {
public BigDecimal calculate(Shipment s) { return s.getWeight(); }
}
전략 패턴 = OCP 구현 도구인 이유는?
답:
1. OCP 구현:
확장:
분기 제거:
vs 분기:
| 항목 | 전략 패턴 | 템플릿 메소드 |
|---|---|---|
| 방식 | 합성 (주입) | 상속 (서브클래스) |
| 변경 | 런타임 | 컴파일 타임 |
| 알고리즘 | 통째 교체 | 일부 재정의 |
| 결합 | 느슨 | 강함 |
합성 vs 상속:
전략 패턴 (합성):
- 전략 객체 보유
- 주입
- 느슨
템플릿 메소드 (상속):
- 서브클래스
- 오버라이드
- 강결합
런타임 vs 컴파일:
전략:
- 런타임 전략 교체
- 동적
템플릿:
- 컴파일 타임 결정
- 정적
선택:
전략 패턴:
- 알고리즘 통째 교체
- 런타임 변경
- 유연성
템플릿 메소드:
- 흐름 고정, 일부만
- 공통 흐름 강제
// 전략 패턴 (합성) vs 템플릿 메소드 (상속)
// 전략 패턴 — 합성 (권장)
class ShipmentDaoStrategy {
private final ConnectionMaker connectionMaker; // 합성
ShipmentDaoStrategy(ConnectionMaker cm) { this.connectionMaker = cm; }
// 런타임 전략 교체 가능
}
// 템플릿 메소드 — 상속
abstract class ShipmentDaoTemplate {
abstract Connection getConnection(); // 상속
// 컴파일 타임 결정
}
// 같은 문제, 다른 접근:
// - 전략: 합성, 런타임, 느슨 (선호)
// - 템플릿: 상속, 컴파일, 강결합
interface ConnectionMaker { Connection makeConnection() throws Exception; }
전략 패턴 vs 템플릿 메소드 패턴의 차이는?
답:
1. 방식:
변경:
알고리즘:
결합:
Spring 전략 패턴 사례:
- Comparator (정렬 전략)
- Resource (리소스 접근 전략)
- PlatformTransactionManager (트랜잭션 전략)
- ViewResolver (뷰 해석 전략)
- 곳곳에 인터페이스 + 주입
// Comparator = 전략 패턴
List<Shipment> shipments = ...;
// 다른 정렬 전략
shipments.sort(Comparator.comparing(Shipment::getWeight)); // 무게
shipments.sort(Comparator.comparing(Shipment::getId)); // ID
// Comparator = 정렬 전략 (교체 가능)
DI 와 전략:
Spring DI:
- 인터페이스 주입
- 전략 패턴 자연스럽게
@Autowired:
- 전략 구현 주입
// Spring 빈으로 전략 주입
@Service
public class ShipmentService {
private final FreightStrategy freightStrategy;
// 생성자 주입 (전략)
public ShipmentService(FreightStrategy freightStrategy) {
this.freightStrategy = freightStrategy;
}
}
@Component
public class SeaFreightStrategy implements FreightStrategy {
public BigDecimal calculate(Shipment s) { return s.getWeight(); }
}
// Spring 이 전략 주입 (DI = 전략 자동화)
// Spring 으로 전략 패턴 (ILIC)
// 전략 인터페이스
public interface FreightStrategy {
BigDecimal calculate(Shipment shipment);
}
// 전략 구현들 (빈)
@Component("sea")
public class SeaFreightStrategy implements FreightStrategy {
public BigDecimal calculate(Shipment s) {
return s.getWeight().multiply(BigDecimal.valueOf(10));
}
}
@Component("air")
public class AirFreightStrategy implements FreightStrategy {
public BigDecimal calculate(Shipment s) {
return s.getWeight().multiply(BigDecimal.valueOf(50));
}
}
// Context (빈 주입)
@Service
public class ShipmentService {
private final Map<String, FreightStrategy> strategies; // 전략 맵
public ShipmentService(Map<String, FreightStrategy> strategies) {
this.strategies = strategies; // Spring 이 모든 전략 주입
}
public BigDecimal calculate(Shipment s, String mode) {
return strategies.get(mode).calculate(s); // 전략 선택
}
}
// Spring DI = 전략 패턴의 자연스러운 구현
Spring의 전략 패턴 사례는?
답:
1. 사례:
Comparator:
DI 와 전략:
빈 주입:
Phase 6 — OCP & 전략 패턴
Unit 6.1 — 인터페이스로 결합도 낮추기
- ConnectionMaker 인터페이스
- 주입, 결합도 ↓
Unit 6.2 — OCP ★깊이
- 확장 열림, 변경 닫힘
- 디자인 패턴의 뿌리
Unit 6.3 — 전략 패턴
- Context/Strategy/ConcreteStrategy
- OCP 구현 도구
진화 과정 (Phase 3~6):
전통 DAO (혼재)
↓ 관심사 분리 (Phase 4)
메서드 추출, 추상클래스
↓ 디자인 패턴 (Phase 5)
템플릿/팩토리 메소드
↓ 인터페이스 + 합성 (Phase 6)
전략 패턴 (OCP, DIP)
↓ (다음)
IoC / DI (Phase 7~8)
Phase 6 핵심 통찰:
1. 인터페이스로 결합도 ↓
2. OCP (확장/변경)
3. 전략 패턴 (Context/Strategy)
4. 합성 > 상속
5. → 구현 결정 외부로 (IoC 향)
Phase 6 → Phase 7:
- 전략 주입 → 제어의 역전
Phase 7 — IoC (7.1 ★깊이):
- IoC 개념 (제어 역전)
- 프레임워크 vs 라이브러리
- IoC 컨테이너
Phase 6의 종합은?
답:
1. 3 Unit:
진화:
핵심:
다음:
| Q | 핵심 답변 |
|---|---|
| 전략 패턴? | 알고리즘 인터페이스 + 주입 |
| 3요소? | Context/Strategy/ConcreteStrategy |
| 매핑? | ShipmentDao/ConnectionMaker/N·D |
| 주입? | 생성자/Setter |
| OCP 구현? | 새 전략 추가 (확장) |
| vs 템플릿? | 합성 vs 상속 |
| Spring 사례? | Comparator, DI |
| 장점? | 런타임 교체, 분기 제거 |
| 분기 제거? | 다형성 |
| 다음? | IoC |
답:
답:
답:
답:
답:
1. 전략 패턴
2. OCP 구현 도구
3. 합성 기반
🌱 Phase 6 — OCP & 전략 패턴
✅ Unit 6.1 인터페이스로 결합도 낮추기
✅ Unit 6.2 OCP (개방폐쇄원칙) ★깊이
✅ Unit 6.3 전략 패턴 ← 여기, Phase 6 완주
→ 인터페이스 (결합도 ↓)
→ OCP (확장/변경)
→ 전략 패턴 (Context/Strategy)
Phase 7 — 제어의 역전 IoC (7.1 ★깊이)
Unit 7.1 — IoC (제어의 역전) 개념 ★깊이
Unit 7.2 — 프레임워크 vs 라이브러리
Unit 7.3 — IoC 컨테이너의 역할
✅ Part A — 동시성 마무리 (7 Unit)
🌱 Part B — 토비의 스프링
✅ Phase 3~5 (9 Unit)
✅ Phase 6 — OCP & 전략 패턴 (3 Unit) ← 완주
⏭ Phase 7 — IoC (3 Unit)
총: 19/26 Unit
🏆 Phase 6 완주 — OCP & 전략 패턴