C++ Strategy 패턴
알고리즘 군을 캡슐화하여 런타임에 교체 가능하게 만드는 패턴입니다. 고전적 인터페이스 방식과 std::function 기반의 경량 방식을 함께 제공합니다.
#include <iostream>
#include <functional>
#include <memory>
#include <vector>
#include <string>
#include <algorithm>
#include <numeric>
#include <stdexcept>
// ════════════════════════════════════════════════════════════════════════════
// 방식 1: 고전적 인터페이스 기반 전략 — 결제 시스템
// ════════════════════════════════════════════════════════════════════════════
// ── 결제 전략 인터페이스 ──────────────────────────────────────────────────────
class PaymentStrategy {
public:
virtual ~PaymentStrategy() = default;
virtual bool pay(double amount) = 0;
virtual std::string name() const = 0;
};
// ── 신용카드 결제 전략 ────────────────────────────────────────────────────────
class CreditCardStrategy final : public PaymentStrategy {
public:
explicit CreditCardStrategy(std::string cardNumber, double limit)
: cardNumber_(std::move(cardNumber)), limit_(limit) {}
bool pay(double amount) override {
if (amount > limit_) {
std::cout << "[신용카드] 한도 초과: " << amount << " > " << limit_ << '\n';
return false;
}
limit_ -= amount;
std::cout << "[신용카드] " << cardNumber_ << " 결제 완료: "
<< amount << "원 (잔여한도: " << limit_ << "원)\n";
return true;
}
std::string name() const override { return "신용카드"; }
private:
std::string cardNumber_;
double limit_;
};
// ── 포인트 결제 전략 ──────────────────────────────────────────────────────────
class PointStrategy final : public PaymentStrategy {
public:
explicit PointStrategy(double points) : points_(points) {}
bool pay(double amount) override {
if (amount > points_) {
std::cout << "[포인트] 잔여 포인트 부족: " << points_ << "P\n";
return false;
}
points_ -= amount;
std::cout << "[포인트] " << amount << "P 사용 (잔여: " << points_ << "P)\n";
return true;
}
std::string name() const override { return "포인트"; }
private:
double points_;
};
// ── 결제 컨텍스트 ─────────────────────────────────────────────────────────────
class ShoppingCart {
public:
void setStrategy(std::shared_ptr<PaymentStrategy> strategy) {
strategy_ = std::move(strategy);
}
void addItem(const std::string& name, double price) {
items_.emplace_back(name, price);
std::cout << "[장바구니] '" << name << "' 추가 (" << price << "원)\n";
}
bool checkout() {
if (!strategy_) throw std::runtime_error("결제 수단이 설정되지 않았습니다.");
double total = std::accumulate(items_.begin(), items_.end(), 0.0,
[](double sum, const auto& item) { return sum + item.second; });
std::cout << "[결제] 총액 " << total << "원 — " << strategy_->name() << " 결제 시도\n";
bool ok = strategy_->pay(total);
if (ok) items_.clear();
return ok;
}
private:
std::shared_ptr<PaymentStrategy> strategy_;
std::vector<std::pair<std::string, double>> items_;
};
// ════════════════════════════════════════════════════════════════════════════
// 방식 2: std::function 경량 전략 — 데이터 압축 파이프라인
// ════════════════════════════════════════════════════════════════════════════
using CompressionStrategy = std::function<std::string(const std::string&)>;
// ── 압축 알고리즘들 (람다/함수로 교체 가능) ────────────────────────────────────
CompressionStrategy makeRLE() {
return [](const std::string& input) -> std::string {
// 간단한 RLE 시뮬레이션
return "[RLE] " + std::to_string(input.size()) + "B → "
+ std::to_string(input.size() / 2) + "B";
};
}
CompressionStrategy makeGzip() {
return [](const std::string& input) -> std::string {
return "[Gzip] " + std::to_string(input.size()) + "B → "
+ std::to_string(input.size() / 4) + "B";
};
}
class DataExporter {
public:
explicit DataExporter(CompressionStrategy strategy)
: compress_(std::move(strategy)) {}
void setStrategy(CompressionStrategy strategy) {
compress_ = std::move(strategy);
}
void exportData(const std::string& data) {
std::cout << "[내보내기] " << compress_(data) << '\n';
}
private:
CompressionStrategy compress_;
};
// ── 사용 예시 ─────────────────────────────────────────────────────────────────
int main() {
std::cout << "=== 결제 전략 ===\n";
ShoppingCart cart;
cart.addItem("노트북", 1'500'000);
cart.addItem("마우스", 35'000);
// 신용카드로 결제 시도 (한도 부족)
cart.setStrategy(std::make_shared<CreditCardStrategy>("1234-****", 1'000'000));
cart.checkout();
// 포인트로 재시도
cart.addItem("노트북", 1'500'000);
cart.addItem("마우스", 35'000);
cart.setStrategy(std::make_shared<PointStrategy>(2'000'000));
cart.checkout();
std::cout << "\n=== 압축 전략 ===\n";
std::string payload(1024, 'x'); // 1KB 더미 데이터
DataExporter exporter(makeRLE());
exporter.exportData(payload); // RLE 압축
exporter.setStrategy(makeGzip());
exporter.exportData(payload); // Gzip 압축
// 런타임 람다 교체 — 무압축 패스스루
exporter.setStrategy([](const std::string& d) {
return "[없음] " + std::to_string(d.size()) + "B (압축 없음)";
});
exporter.exportData(payload);
}