C++ · 심층 가이드
C++ 패턴 완전 정리
전략과 옵저버, 데코레이터를 포함한 20개 설계 패턴 예제를 스마트 포인터 소유권과 정적 다형성이라는 C++ 고유의 관점에서 처음부터 다시 씁니다.
고전 디자인 패턴 책의 예제를 C++로 그대로 옮기면 대개 과하게 무거워집니다. 인터페이스와 가상 호출로만 풀던 문제 중 상당수를 이 언어는 템플릿과 std::function으로 더 얇게 해결하기 때문입니다. 전략을 클래스 계층으로 만들 것인지 호출 가능 객체 하나로 받을 것인지, 다형성을 런타임에 둘 것인지 컴파일 타임에 둘 것인지가 매번 선택지로 남습니다. 여기에 C++만의 축이 하나 더 붙는데, 바로 각 참여 객체를 누가 소유하느냐입니다.
전략 패턴과 커맨드는 결국 호출 가능 객체를 저장하는 같은 뼈대라 나란히 보면 차이가 선명하고, 데코레이터와 미들웨어 체인도 감싸는 방향만 다른 한 쌍입니다. 캐시 래퍼와 retry 유틸은 데코레이터의 실용적인 사례이므로 그 뒤에 이어 두는 편이 자연스럽습니다. 저장소 패턴과 의존성 주입 컨테이너, 파사드는 앞의 객체지향 편에서 만든 인터페이스 설계를 그대로 확장한 것이니 함께 읽으면 반복이 줄어듭니다.
옵저버는 C++에서 수명 관리로 무너지는 대표적인 패턴입니다. 구독자를 원시 포인터로 담아 둔 채 그 객체가 먼저 소멸하면, 다음 알림에서 이미 없는 대상을 호출하게 됩니다. 구독 해지를 잊지 않는 규율에 기대는 대신 std::weak_ptr로 보관하고 알림 시점에 lock()으로 살아 있는지 확인하는 편이 현실적입니다. 반대로 부모와 자식이 서로 std::shared_ptr을 쥐면 참조 횟수가 0이 되지 않아 조용히 새므로, 역방향 링크는 weak_ptr로 끊어 두어야 합니다.
01설정 객체
C++에서 설정 객체 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
C++ code
// 설정 객체
#include <iostream>
int percent(int value) { return static_cast<int>(value * 0.9); }
int flat(int value) { return value - 5; }
int main() {
auto strategy = percent;
std::cout << strategy(100) << "\n";
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
02전략 패턴
C++에서 전략 패턴 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
C++ code
// 전략 패턴
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<int, std::string> cache;
if (!cache.count(2)) cache[2] = "user";
std::cout << cache[2] << "\n";
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
03옵저버
C++에서 옵저버 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
C++ code
// 옵저버
#include <iostream>
int percent(int value) { return static_cast<int>(value * 0.9); }
int flat(int value) { return value - 5; }
int main() {
auto strategy = percent;
std::cout << strategy(100) << "\n";
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
04데코레이터
C++에서 데코레이터 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
C++ code
// 데코레이터
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<int, std::string> cache;
if (!cache.count(4)) cache[4] = "user";
std::cout << cache[4] << "\n";
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
05어댑터
C++에서 어댑터 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
C++ code
// 어댑터
#include <iostream>
int percent(int value) { return static_cast<int>(value * 0.9); }
int flat(int value) { return value - 5; }
int main() {
auto strategy = percent;
std::cout << strategy(100) << "\n";
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
06커맨드
C++에서 커맨드 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
C++ code
// 커맨드
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<int, std::string> cache;
if (!cache.count(6)) cache[6] = "user";
std::cout << cache[6] << "\n";
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
07상태 패턴
C++에서 상태 패턴 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
C++ code
// 상태 패턴
#include <iostream>
int percent(int value) { return static_cast<int>(value * 0.9); }
int flat(int value) { return value - 5; }
int main() {
auto strategy = percent;
std::cout << strategy(100) << "\n";
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
08팩터리 메서드
C++에서 팩터리 메서드 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
C++ code
// 팩터리 메서드
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<int, std::string> cache;
if (!cache.count(8)) cache[8] = "user";
std::cout << cache[8] << "\n";
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
09빌더 패턴
C++에서 빌더 패턴 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
C++ code
// 빌더 패턴
#include <iostream>
int percent(int value) { return static_cast<int>(value * 0.9); }
int flat(int value) { return value - 5; }
int main() {
auto strategy = percent;
std::cout << strategy(100) << "\n";
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
10캐시 래퍼
C++에서 캐시 래퍼 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
C++ code
// 캐시 래퍼
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<int, std::string> cache;
if (!cache.count(10)) cache[10] = "user";
std::cout << cache[10] << "\n";
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
11retry 유틸
C++에서 retry 유틸 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
C++ code
// retry 유틸
#include <iostream>
int percent(int value) { return static_cast<int>(value * 0.9); }
int flat(int value) { return value - 5; }
int main() {
auto strategy = percent;
std::cout << strategy(100) << "\n";
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
12미들웨어 체인
C++에서 미들웨어 체인 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
C++ code
// 미들웨어 체인
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<int, std::string> cache;
if (!cache.count(12)) cache[12] = "user";
std::cout << cache[12] << "\n";
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
13검증기 조합
C++에서 검증기 조합 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
C++ code
// 검증기 조합
#include <iostream>
int percent(int value) { return static_cast<int>(value * 0.9); }
int flat(int value) { return value - 5; }
int main() {
auto strategy = percent;
std::cout << strategy(100) << "\n";
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
14포매터 파이프
C++에서 포매터 파이프 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
C++ code
// 포매터 파이프
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<int, std::string> cache;
if (!cache.count(14)) cache[14] = "user";
std::cout << cache[14] << "\n";
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
15페이지네이션
C++에서 페이지네이션 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
C++ code
// 페이지네이션
#include <iostream>
int percent(int value) { return static_cast<int>(value * 0.9); }
int flat(int value) { return value - 5; }
int main() {
auto strategy = percent;
std::cout << strategy(100) << "\n";
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
16데이터 매퍼
C++에서 데이터 매퍼 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
C++ code
// 데이터 매퍼
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<int, std::string> cache;
if (!cache.count(16)) cache[16] = "user";
std::cout << cache[16] << "\n";
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
17저장소 패턴
C++에서 저장소 패턴 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
C++ code
// 저장소 패턴
#include <iostream>
int percent(int value) { return static_cast<int>(value * 0.9); }
int flat(int value) { return value - 5; }
int main() {
auto strategy = percent;
std::cout << strategy(100) << "\n";
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
18의존성 주입 컨테이너
C++에서 의존성 주입 컨테이너 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
C++ code
// 의존성 주입 컨테이너
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<int, std::string> cache;
if (!cache.count(18)) cache[18] = "user";
std::cout << cache[18] << "\n";
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
19템플릿 메서드
C++에서 템플릿 메서드 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
C++ code
// 템플릿 메서드
#include <iostream>
int percent(int value) { return static_cast<int>(value * 0.9); }
int flat(int value) { return value - 5; }
int main() {
auto strategy = percent;
std::cout << strategy(100) << "\n";
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
20파사드
C++에서 파사드 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
C++ code
// 파사드
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<int, std::string> cache;
if (!cache.count(20)) cache[20] = "user";
std::cout << cache[20] << "\n";
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
정리하며
- 옵저버의 구독자 목록은 weak_ptr로 담고 알림 직전 lock으로 생존을 확인합니다
- shared_ptr 순환 참조는 누수로 이어지므로 역방향 링크는 weak_ptr로 끊습니다
- 전략과 커맨드는 클래스 계층 없이 std::function 하나로 충분한 경우가 많습니다
- 다형성을 런타임 가상 호출로 둘지 템플릿으로 컴파일 타임에 둘지 매번 판단합니다
더 깊이 들어가고 싶다면 C++ 학습 라이브러리에서 다른 주제 가이드를 이어서 보거나, 언어 비교에서 같은 개념이 다른 언어에서 어떻게 표현되는지 확인해 보세요.