디자인 패턴은 소프트웨어 설계 시 반복적으로 나타나는 문제에 대한 재사용 가능한 해결책이다.
1994년 GoF(Gang of Four)가 저서 "Design Patterns: Elements of Reusable Object-Oriented Software"에서 23가지 패턴을 체계화하면서 널리 알려졌다. 디자인 패턴은 특정 코드가 아니라 설계 수준의 템플릿으로, 상황에 맞게 구현 방식을 조정할 수 있다.
객체지향 설계에서 반복적으로 마주치는 문제들:
- 객체 생성 방식의 복잡성
- 클래스 간 강한 결합으로 인한 변경 어려움
- 새로운 기능 추가 시 기존 코드 수정 필요
| 가치 | 설명 |
|---|---|
| 재사용성 | 검증된 설계를 반복 적용 가능 |
| 유지보수성 | 변경에 유연하고 확장 용이한 구조 |
| 의사소통 | 개발자 간 공통 어휘로 설계 의도 전달 |
| 품질 향상 | 오랜 시간 검증된 Best Practice 적용 |
GoF 디자인 패턴은 목적에 따라 생성(Creational), 구조(Structural), 행위(Behavioral) 3가지로 분류된다.
객체 생성 과정을 추상화하여 생성 로직의 복잡성을 숨기고 유연성을 확보한다.
| 패턴 | 설명 | 사용 예시 |
|---|---|---|
| Singleton | 클래스의 인스턴스를 하나만 생성하고 전역 접근점 제공 | 설정 관리, 커넥션 풀 |
| Factory Method | 객체 생성을 서브클래스에 위임하여 생성할 객체 타입을 결정 | 문서 편집기의 다양한 문서 타입 생성 |
| Abstract Factory | 관련된 객체들의 군(family)을 생성하는 인터페이스 제공 | UI 테마별 컴포넌트 생성 |
| Builder | 복잡한 객체의 생성 과정을 단계별로 분리 | 다양한 옵션의 객체 조립 |
| Prototype | 기존 객체를 복사하여 새 객체 생성 | 객체 복제가 빈번한 경우 |
클래스나 객체를 조합하여 더 큰 구조를 형성하고 확장성을 확보한다.
| 패턴 | 설명 | 사용 예시 |
|---|---|---|
| Adapter | 호환되지 않는 인터페이스를 변환하여 함께 동작하게 함 | 레거시 시스템 통합 |
| Bridge | 추상화와 구현을 분리하여 독립적으로 확장 가능하게 함 | 플랫폼 독립적 UI |
| Composite | 객체들을 트리 구조로 구성하여 개별/복합 객체를 동일하게 처리 | 파일 시스템, UI 컴포넌트 |
| Decorator | 객체에 동적으로 책임(기능)을 추가 | Java I/O 스트림 |
| Facade | 복잡한 서브시스템에 단순화된 인터페이스 제공 | 라이브러리 래퍼 |
| Flyweight | 공유를 통해 다수의 유사 객체를 효율적으로 지원 | 문자 렌더링, 게임 오브젝트 |
| Proxy | 다른 객체에 대한 접근을 제어하는 대리 객체 제공 | 지연 로딩, 접근 제어, 캐싱 |
객체 간 책임 분배와 알고리즘 캡슐화를 통해 유연한 상호작용을 가능하게 한다.
| 패턴 | 설명 | 사용 예시 |
|---|---|---|
| Strategy | 알고리즘군을 정의하고 런타임에 교체 가능하게 함 | 정렬 알고리즘, 결제 방식 |
| Template Method | 알고리즘의 골격을 정의하고 일부 단계를 서브클래스에서 구현 | 프레임워크 훅 메서드 |
| Observer | 객체 상태 변경 시 의존 객체들에게 자동 통지 | 이벤트 시스템, 구독 모델 |
| State | 객체의 내부 상태에 따라 행동을 변경 | 주문 상태, 게임 캐릭터 상태 |
| Command | 요청을 객체로 캡슐화하여 매개변수화, 큐잉, 로깅 가능 | 실행 취소/재실행, 매크로 |
| Chain of Responsibility | 요청을 처리할 수 있는 객체들의 체인을 구성 | 이벤트 버블링, 미들웨어 |
| Mediator | 객체들 간의 상호작용을 중재자 객체가 캡슐화 | 채팅룸, GUI 컴포넌트 조정 |
| Memento | 객체의 상태를 저장하고 이전 상태로 복원 | 실행 취소 기능 |
| Visitor | 객체 구조에 새로운 연산을 추가 (객체 수정 없이) | 컴파일러 AST 처리 |
| Iterator | 컬렉션 요소를 순차적으로 접근하는 방법 제공 | Java Iterator, for-each |
| Interpreter | 언어의 문법을 정의하고 해석하는 인터프리터 제공 | SQL 파서, 정규식 |
패턴은 문제가 있을 때 적용한다. 단순한 문제에 복잡한 패턴을 적용하면 오히려 코드가 복잡해진다.
// ❌ 단순한 객체 생성에 팩토리 패턴 적용
UserFactory.createUser("kim")
// ✅ 직접 생성으로 충분
User("kim")
"패턴을 적용하기 위해" 코드를 작성하면 안 된다. 해결하려는 문제가 먼저 있고, 그에 맞는 패턴을 선택해야 한다.
같은 패턴이라도 언어와 프레임워크에 따라 구현 방식이 달라진다. 패턴의 의도를 이해하고 상황에 맞게 적용해야 한다.
- Refactoring Guru - Design Patterns - 패턴별 상세 설명과 다양한 언어 예제