[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -2,94 +2,186 @@
|
||||
id: wiki-2026-0508-switch-statements-switch-문
|
||||
title: Switch Statements (Switch 문)
|
||||
category: 10_Wiki/Topics
|
||||
status: needs_review
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: []
|
||||
aliases: [Switch Statement, Pattern Matching, Replace Switch]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.92
|
||||
tags: [uncategorized]
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [refactoring, code-smell, polymorphism]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-08
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
inferred_by: Claude Opus 4.7 (auto-normalize 2026-05-08)
|
||||
tech_stack:
|
||||
language: unspecified
|
||||
framework: unspecified
|
||||
language: multi
|
||||
framework: refactoring
|
||||
---
|
||||
|
||||
# [[Switch Statements (Switch 문)]]
|
||||
# Switch Statements (Switch 문)
|
||||
|
||||
## 📌 한 줄 통찰 (The Karpathy Summary)
|
||||
Switch 문은 객체지향 프로그래밍에서 상속이나 다형성(Polymorphism)의 이점을 제대로 살리지 못하고 절차지향적으로 코드가 구현되었음을 나타내는 대표적인 '코드 스멜(OO Abusers)' 중 하나입니다 [1-3]. 이러한 조건문은 프로그램 여러 곳에 흩어져 코드 중복을 유발하며, 새로운 조건이 추가될 때마다 관련된 모든 Switch 문을 찾아 수정해야 하는 구조적 결함을 낳습니다 [2]. 일반적으로 '조건식을 다형성으로 바꾸기(Replace Conditional with Polymorphism)'와 같은 리팩토링 기법을 통해 이 문제를 해결하고 시스템의 유지보수성을 향상시킬 수 있습니다 [4, 5].
|
||||
## 매 한 줄
|
||||
> **"매 type-tagged switch 의 polymorphism 의 missed opportunity"**. 매 classic code smell 의 — 매 type 의 switch 의 새 type 의 추가 시 매 모든 switch 의 update 의 필요. 2026 의 modern lang 의 sealed types + pattern matching 의 acceptable.
|
||||
|
||||
## 📖 구조화된 지식 (Synthesized Content)
|
||||
* **객체지향 설계의 결함 지표:** 객체지향 코드의 가장 뚜렷한 특징 중 하나는 Switch(또는 case) 문이 상대적으로 적다는 것입니다 [2]. Switch 문이 반복적으로 등장하는 것은 객체지향 구성 요소를 남용했거나(OO Abusers) 잘못 사용했다는 신호로 간주됩니다 [1, 3]. Switch 문의 근본적인 문제는 중복을 발생시킨다는 점이며, 새로운 분기 조건이 생길 때마다 흩어진 코드를 모두 수정해야 하므로 변경을 방해합니다 [2].
|
||||
* **다형성을 활용한 리팩토링:** Switch 문을 마주치면 가장 먼저 다형성의 적용을 고려해야 합니다 [4].
|
||||
* Switch 문이 종종 타입 코드(Type Code)를 기반으로 작동하는 경우, 우선 '함수 추출하기(Extract Method)'와 '함수 옮기기(Move Method)'를 사용해 해당 Switch 문을 다형성이 필요한 클래스로 이동시킵니다 [4].
|
||||
* 그 다음 '타입 코드를 서브클래스로 바꾸기(Replace Type Code with Subclasses)'나 '타입 코드를 상태/전략 패턴으로 바꾸기(Replace Type Code with State/Strategy)'를 통해 상속 구조를 구축합니다 [4].
|
||||
* 마지막으로 '조건식을 다형성으로 바꾸기(Replace Conditional with Polymorphism)'를 적용하여 분기 로직을 동적 바인딩으로 대체합니다 [4, 5]. 이를 통해 새로운 타입이 추가될 때 기존 코드를 수정할 필요가 없게 되어 개방-폐쇄 원칙(Open-Closed Principle)을 준수할 수 있습니다 [5, 6].
|
||||
* **다형성 외의 대안 기법:** 조건이 Null인 경우를 처리하는 분기가 있다면 '널 객체 도입하기(Introduce Null Object)' 기법을 활용하여 Switch 문의 복잡성을 덜어낼 수 있습니다 [7].
|
||||
## 매 핵심
|
||||
|
||||
## ⚠️ 모순 및 업데이트 (Contradictions & Updates)
|
||||
* **다형성 적용의 오버엔지니어링(Overkill) 위험:** Switch 문을 제거하기 위해 무조건 다형성을 도입하는 것은 주의해야 합니다. 단일 메서드에만 영향을 미치는 소수의 조건 케이스만 존재하고 향후 이 조건들이 변경될 것으로 예상되지 않는 경우, 다형성을 도입하는 것은 오히려 불필요한 복잡성을 가중시키는 오버엔지니어링(Overkill)이 될 수 있습니다 [7].
|
||||
* 이러한 제약 상황에서는 다형성 대신 '매개변수를 명시적 메서드로 바꾸기(Replace Parameter with Explicit Methods)'와 같이 더 단순한 리팩토링 방식을 선택하는 것이 타당합니다 [7].
|
||||
### 매 왜 smell 인가
|
||||
- **OCP 위반**: 매 새 type 의 추가 의 매 switch 의 modify 의 필요.
|
||||
- **Shotgun surgery**: 매 동일 switch 의 codebase 의 scatter.
|
||||
- **Type safety hole**: 매 default 의 fallthrough — 매 unhandled case 의 silent.
|
||||
|
||||
---
|
||||
*Last updated: 2026-05-03*
|
||||
### 매 acceptable cases
|
||||
- Sealed/exhaustive matching (Kotlin, Rust, Swift, Java 21+).
|
||||
- Single switch — 매 dispatch 의 한 곳.
|
||||
- Performance-critical hot path (jump table).
|
||||
- Parsing / state machines.
|
||||
|
||||
## 🤖 LLM 활용 힌트 (How to Use This Knowledge)
|
||||
### 매 refactoring 전략
|
||||
- **Replace with polymorphism**: type-tagged switch → method override.
|
||||
- **Replace with strategy**: behavior-tagged switch → strategy injection.
|
||||
- **Replace with map**: data-tagged switch → lookup table.
|
||||
- **Sealed pattern match**: 매 modern lang 의 exhaustive 의 보장.
|
||||
|
||||
**언제 이 지식을 쓰는가:**
|
||||
- *(TODO)*
|
||||
### 매 응용
|
||||
1. AST visitor (legitimate switch).
|
||||
2. State machine (legitimate).
|
||||
3. Domain logic dispatch (refactor to polymorphism).
|
||||
|
||||
**언제 쓰면 안 되는가:**
|
||||
- *(TODO)*
|
||||
## 💻 패턴
|
||||
|
||||
## 🧪 검증 상태 (Validation)
|
||||
|
||||
- **정보 상태:** needs_review
|
||||
- **출처 신뢰도:** A
|
||||
- **검토 이유:** *(P-Reinforce Phase 1 자동 정규화. 본문 검증 필요.)*
|
||||
|
||||
## 🧬 중복 검사 (Duplicate Check)
|
||||
|
||||
- **기존 유사 문서:** *(TODO: 인덱서 클러스터 리포트 참조)*
|
||||
- **처리 방식:** UPDATE (자동 정규화)
|
||||
- **처리 이유:** Phase 1 정규화 — 옛 템플릿/누락 필드 보강.
|
||||
|
||||
## 🔗 지식 연결 (Graph)
|
||||
|
||||
- **Parent:** [[10_Wiki/Topics]]
|
||||
- **Related:** *(TODO: 최소 2개)*
|
||||
- **Opposite / Trade-off:** *(TODO)*
|
||||
- **Raw Source:** 직접 입력
|
||||
|
||||
## 🕓 변경 이력 (Changelog)
|
||||
|
||||
| 날짜 | 변경 내용 | 처리 방식 | 신뢰도 |
|
||||
|------|-----------|-----------|--------|
|
||||
| 2026-05-08 | P-Reinforce Phase 1 정규화 (frontmatter + 헤더 표준화) | UPDATE | A |
|
||||
|
||||
## 💻 코드 패턴 (Code Patterns)
|
||||
|
||||
**패턴 1:** *(TODO: 이 프로젝트 컨벤션 반영한 구조 스켈레톤)*
|
||||
|
||||
```text
|
||||
# TODO
|
||||
### Smell — type-tagged switch
|
||||
```java
|
||||
// BAD
|
||||
double area(Shape s) {
|
||||
switch (s.type) {
|
||||
case CIRCLE: return Math.PI * s.radius * s.radius;
|
||||
case SQUARE: return s.side * s.side;
|
||||
case TRIANGLE: return 0.5 * s.base * s.height;
|
||||
default: throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🤔 의사결정 기준 (Decision Criteria)
|
||||
### Refactor — polymorphism
|
||||
```java
|
||||
sealed interface Shape permits Circle, Square, Triangle {
|
||||
double area();
|
||||
}
|
||||
record Circle(double r) implements Shape {
|
||||
public double area() { return Math.PI * r * r; }
|
||||
}
|
||||
record Square(double side) implements Shape {
|
||||
public double area() { return side * side; }
|
||||
}
|
||||
record Triangle(double base, double h) implements Shape {
|
||||
public double area() { return 0.5 * base * h; }
|
||||
}
|
||||
// caller: shape.area()
|
||||
```
|
||||
|
||||
**선택 A를 써야 할 때:**
|
||||
- *(TODO)*
|
||||
### Java 21 — pattern matching switch (exhaustive)
|
||||
```java
|
||||
double area(Shape s) {
|
||||
return switch (s) {
|
||||
case Circle c -> Math.PI * c.r() * c.r();
|
||||
case Square sq -> sq.side() * sq.side();
|
||||
case Triangle t -> 0.5 * t.base() * t.h();
|
||||
// no default — compiler proves exhaustive
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
**선택 B를 써야 할 때:**
|
||||
- *(TODO)*
|
||||
### Kotlin — sealed when
|
||||
```kotlin
|
||||
sealed class Event
|
||||
data class Click(val x: Int, val y: Int) : Event()
|
||||
data class Key(val code: Int) : Event()
|
||||
object Close : Event()
|
||||
|
||||
**기본값:**
|
||||
> *(TODO)*
|
||||
fun handle(e: Event) = when (e) {
|
||||
is Click -> "click at ${e.x},${e.y}"
|
||||
is Key -> "key ${e.code}"
|
||||
Close -> "bye"
|
||||
} // exhaustive — no else needed
|
||||
```
|
||||
|
||||
## ❌ 안티패턴 (Anti-Patterns)
|
||||
### Rust — match
|
||||
```rust
|
||||
enum Msg { Quit, Move { x: i32, y: i32 }, Write(String) }
|
||||
|
||||
- **[안티패턴]:** *(TODO: 무엇을 하면 안 되는가 + 이유 + 대신 무엇을)*
|
||||
fn process(m: Msg) {
|
||||
match m {
|
||||
Msg::Quit => println!("quit"),
|
||||
Msg::Move { x, y } => println!("move to {x},{y}"),
|
||||
Msg::Write(s) => println!("write {s}"),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Replace with map (data dispatch)
|
||||
```typescript
|
||||
// BAD
|
||||
function fmt(t: string, v: number): string {
|
||||
switch (t) {
|
||||
case "usd": return `$${v}`;
|
||||
case "eur": return `€${v}`;
|
||||
case "krw": return `₩${v}`;
|
||||
}
|
||||
}
|
||||
|
||||
// GOOD
|
||||
const PREFIX: Record<string, string> = { usd: "$", eur: "€", krw: "₩" };
|
||||
const fmt = (t: string, v: number) => `${PREFIX[t] ?? ""}${v}`;
|
||||
```
|
||||
|
||||
### Replace with strategy
|
||||
```python
|
||||
class Discount(Protocol):
|
||||
def apply(self, price: float) -> float: ...
|
||||
|
||||
class Pct(Discount):
|
||||
def __init__(self, p): self.p = p
|
||||
def apply(self, price): return price * (1 - self.p)
|
||||
|
||||
class Fixed(Discount):
|
||||
def __init__(self, amt): self.amt = amt
|
||||
def apply(self, price): return max(0, price - self.amt)
|
||||
|
||||
# caller injects Discount, no switch
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Type dispatch (open set) | Polymorphism |
|
||||
| Type dispatch (closed set) | Sealed + pattern match |
|
||||
| Data lookup | Map / dict |
|
||||
| Complex behavior swap | Strategy pattern |
|
||||
| AST / state machine | Switch (legitimate) |
|
||||
|
||||
**기본값**: 매 sealed types + exhaustive pattern matching — 매 modern lang 의 ergonomic + safe.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Code Smells]] · [[Refactoring]]
|
||||
- 변형: [[Pattern Matching]] · [[Polymorphism]] · [[Strategy Pattern]]
|
||||
- 응용: [[State Machine]] · [[Visitor Pattern]] · [[ADT]]
|
||||
- Adjacent: [[Open-Closed Principle]] · [[Sealed Classes]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 switch 의 detect 의 refactor suggestion, type 의 sealed 의 propose.
|
||||
**언제 X**: legitimate switch (AST, state machine) 의 전체 refactor 의 — 매 false positive.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Refactor every switch**: 매 legitimate cases 의 보존 — AST, parser.
|
||||
- **Visitor for trivial dispatch**: 매 over-engineering — 매 polymorphism 의 충분.
|
||||
- **Strategy without need**: 매 single-impl 의 strategy 의 yagni.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Fowler, "Refactoring" 2nd ed.; Effective Java 3rd ed.; JEP 441).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — refactor patterns, modern pattern matching |
|
||||
|
||||
Reference in New Issue
Block a user