Serie: C++
cpp
636 líneas
· Actualizado 2026-04-03
strategy.cpp
C++/Part5_進階主題/Ch24_設計模式與最佳實踐/strategy.cpp
// strategy.cpp
// 編譯指令:g++ -std=c++17 -Wall strategy.cpp -o strategy
//
// 本程式示範 Strategy 模式:傳統類別繼承方式與現代 std::function 方式
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
#include <memory>
#include <numeric>
#include <sstream>
#include <cmath>
#include <map>
#include <iomanip>
// ============================================================
// 第一部分:傳統 Strategy 模式(排序策略)
// ============================================================
// 策略介面
class SortStrategy {
public:
virtual ~SortStrategy() = default;
virtual void sort(std::vector<int>& data) = 0;
virtual std::string name() const = 0;
};
// 具體策略 1:泡沫排序
class BubbleSort : public SortStrategy {
public:
void sort(std::vector<int>& data) override {
for (std::size_t i = 0; i < data.size(); ++i) {
for (std::size_t j = 0; j + 1 < data.size() - i; ++j) {
if (data[j] > data[j + 1]) {
std::swap(data[j], data[j + 1]);
}
}
}
}
std::string name() const override { return "泡沫排序 (Bubble Sort)"; }
};
// 具體策略 2:選擇排序
class SelectionSort : public SortStrategy {
public:
void sort(std::vector<int>& data) override {
for (std::size_t i = 0; i < data.size(); ++i) {
std::size_t min_idx = i;
for (std::size_t j = i + 1; j < data.size(); ++j) {
if (data[j] < data[min_idx]) min_idx = j;
}
std::swap(data[i], data[min_idx]);
}
}
std::string name() const override { return "選擇排序 (Selection Sort)"; }
};
// 具體策略 3:標準函式庫排序
class StdSort : public SortStrategy {
public:
void sort(std::vector<int>& data) override {
std::sort(data.begin(), data.end());
}
std::string name() const override { return "標準排序 (std::sort)"; }
};
// Context 類別
class Sorter {
std::unique_ptr<SortStrategy> strategy_;
public:
void set_strategy(std::unique_ptr<SortStrategy> strategy) {
strategy_ = std::move(strategy);
}
void sort(std::vector<int>& data) {
if (!strategy_) {
std::cout << " 錯誤:未設定排序策略!\n";
return;
}
std::cout << " 使用策略: " << strategy_->name() << "\n";
strategy_->sort(data);
}
};
void print_vector(const std::string& label, const std::vector<int>& v) {
std::cout << " " << label << ": [";
for (std::size_t i = 0; i < v.size(); ++i) {
if (i > 0) std::cout << ", ";
std::cout << v[i];
}
std::cout << "]\n";
}
void demo_sort_strategy() {
std::cout << "========================================\n";
std::cout << " 傳統 Strategy:排序策略\n";
std::cout << "========================================\n\n";
Sorter sorter;
std::vector<int> original = {64, 34, 25, 12, 22, 11, 90};
// 策略 1:泡沫排序
auto data1 = original;
sorter.set_strategy(std::make_unique<BubbleSort>());
print_vector("排序前", data1);
sorter.sort(data1);
print_vector("排序後", data1);
// 策略 2:選擇排序
std::cout << "\n";
auto data2 = original;
sorter.set_strategy(std::make_unique<SelectionSort>());
print_vector("排序前", data2);
sorter.sort(data2);
print_vector("排序後", data2);
// 策略 3:std::sort
std::cout << "\n";
auto data3 = original;
sorter.set_strategy(std::make_unique<StdSort>());
print_vector("排序前", data3);
sorter.sort(data3);
print_vector("排序後", data3);
std::cout << "\n 三種策略的結果應該相同,但效能不同\n\n";
}
// ============================================================
// 第二部分:使用 std::function 的 Strategy(更簡潔)
// ============================================================
class ModernSorter {
using SortFunc = std::function<void(std::vector<int>&)>;
SortFunc strategy_;
std::string strategy_name_;
public:
void set_strategy(const std::string& name, SortFunc func) {
strategy_name_ = name;
strategy_ = std::move(func);
}
void sort(std::vector<int>& data) {
if (!strategy_) {
std::cout << " 錯誤:未設定策略!\n";
return;
}
std::cout << " 使用策略: " << strategy_name_ << "\n";
strategy_(data);
}
};
void demo_modern_sort_strategy() {
std::cout << "========================================\n";
std::cout << " 現代 Strategy:std::function\n";
std::cout << "========================================\n\n";
ModernSorter sorter;
std::vector<int> data = {50, 20, 80, 10, 40, 60, 30};
// 使用 lambda 設定策略
sorter.set_strategy("升序排序", [](std::vector<int>& v) {
std::sort(v.begin(), v.end());
});
auto d1 = data;
sorter.sort(d1);
print_vector("結果", d1);
// 切換策略
std::cout << "\n";
sorter.set_strategy("降序排序", [](std::vector<int>& v) {
std::sort(v.begin(), v.end(), std::greater<>());
});
auto d2 = data;
sorter.sort(d2);
print_vector("結果", d2);
// 自定義策略:只排序偶數,奇數放後面
std::cout << "\n";
sorter.set_strategy("偶數優先", [](std::vector<int>& v) {
std::stable_partition(v.begin(), v.end(), [](int x) { return x % 2 == 0; });
});
auto d3 = data;
sorter.sort(d3);
print_vector("結果", d3);
std::cout << "\n std::function 版本不需要定義類別,更加靈活\n\n";
}
// ============================================================
// 第三部分:付款策略
// ============================================================
class PaymentStrategy {
public:
virtual ~PaymentStrategy() = default;
virtual bool pay(double amount) = 0;
virtual std::string description() const = 0;
};
class CreditCardPayment : public PaymentStrategy {
std::string card_number_;
double credit_limit_;
double used_ = 0;
public:
CreditCardPayment(const std::string& card, double limit)
: card_number_(card), credit_limit_(limit) {}
bool pay(double amount) override {
if (used_ + amount > credit_limit_) {
std::cout << " [信用卡] 額度不足!已使用 " << used_
<< " / " << credit_limit_ << "\n";
return false;
}
used_ += amount;
std::cout << " [信用卡] 以卡號 ****"
<< card_number_.substr(card_number_.size() - 4)
<< " 付款 $" << std::fixed << std::setprecision(2) << amount
<< " (剩餘額度: $" << (credit_limit_ - used_) << ")\n";
return true;
}
std::string description() const override {
return "信用卡 ****" + card_number_.substr(card_number_.size() - 4);
}
};
class BankTransferPayment : public PaymentStrategy {
std::string account_;
double balance_;
public:
BankTransferPayment(const std::string& account, double balance)
: account_(account), balance_(balance) {}
bool pay(double amount) override {
if (amount > balance_) {
std::cout << " [銀行轉帳] 餘額不足!\n";
return false;
}
balance_ -= amount;
std::cout << " [銀行轉帳] 從帳戶 " << account_
<< " 轉出 $" << std::fixed << std::setprecision(2) << amount
<< " (餘額: $" << balance_ << ")\n";
return true;
}
std::string description() const override {
return "銀行帳戶 " + account_;
}
};
class DigitalWalletPayment : public PaymentStrategy {
std::string wallet_id_;
double balance_;
public:
DigitalWalletPayment(const std::string& id, double balance)
: wallet_id_(id), balance_(balance) {}
bool pay(double amount) override {
if (amount > balance_) {
std::cout << " [電子錢包] 餘額不足!\n";
return false;
}
balance_ -= amount;
std::cout << " [電子錢包] " << wallet_id_
<< " 付款 $" << std::fixed << std::setprecision(2) << amount
<< " (餘額: $" << balance_ << ")\n";
return true;
}
std::string description() const override {
return "電子錢包 " + wallet_id_;
}
};
class ShoppingCart {
struct Item {
std::string name;
double price;
int quantity;
};
std::vector<Item> items_;
std::unique_ptr<PaymentStrategy> payment_;
public:
void add_item(const std::string& name, double price, int qty = 1) {
items_.push_back({name, price, qty});
}
void set_payment(std::unique_ptr<PaymentStrategy> payment) {
payment_ = std::move(payment);
}
double total() const {
double sum = 0;
for (const auto& item : items_) {
sum += item.price * item.quantity;
}
return sum;
}
void checkout() {
if (items_.empty()) {
std::cout << " 購物車是空的!\n";
return;
}
std::cout << " ┌─────────────────────────────┐\n";
std::cout << " │ 購物明細 │\n";
std::cout << " ├─────────────────────────────┤\n";
for (const auto& item : items_) {
std::cout << " │ " << std::left << std::setw(12) << item.name
<< " x" << item.quantity
<< std::right << std::setw(10) << std::fixed << std::setprecision(2)
<< (item.price * item.quantity) << " │\n";
}
std::cout << " ├─────────────────────────────┤\n";
std::cout << " │ 總計" << std::right << std::setw(20)
<< std::fixed << std::setprecision(2) << total() << " │\n";
std::cout << " └─────────────────────────────┘\n";
if (!payment_) {
std::cout << " 錯誤:未設定付款方式!\n";
return;
}
std::cout << " 付款方式: " << payment_->description() << "\n";
if (payment_->pay(total())) {
std::cout << " 付款成功!✓\n";
items_.clear();
} else {
std::cout << " 付款失敗!✗\n";
}
}
};
void demo_payment_strategy() {
std::cout << "========================================\n";
std::cout << " Strategy 模式:付款策略\n";
std::cout << "========================================\n\n";
ShoppingCart cart;
cart.add_item("C++ 教科書", 1200.0);
cart.add_item("機械鍵盤", 3500.0);
cart.add_item("USB-C 線材", 250.0, 2);
// 嘗試用信用卡付款
std::cout << "--- 使用信用卡付款 ---\n";
cart.set_payment(std::make_unique<CreditCardPayment>("1234567890123456", 10000.0));
cart.checkout();
// 再次購物,使用銀行轉帳
std::cout << "\n--- 使用銀行轉帳付款 ---\n";
cart.add_item("27 吋螢幕", 8500.0);
cart.add_item("螢幕支架", 1200.0);
cart.set_payment(std::make_unique<BankTransferPayment>("012-345678", 20000.0));
cart.checkout();
// 使用電子錢包(餘額不足)
std::cout << "\n--- 使用電子錢包付款(餘額不足)---\n";
cart.add_item("高階耳機", 15000.0);
cart.set_payment(std::make_unique<DigitalWalletPayment>("wallet@user", 5000.0));
cart.checkout();
// 切換到銀行轉帳
std::cout << "\n--- 切換付款方式 ---\n";
cart.set_payment(std::make_unique<BankTransferPayment>("012-345678", 20000.0));
cart.checkout();
std::cout << "\n";
}
// ============================================================
// 第四部分:壓縮策略
// ============================================================
class TextCompressor {
public:
using CompressFunc = std::function<std::string(const std::string&)>;
using DecompressFunc = std::function<std::string(const std::string&)>;
struct Strategy {
std::string name;
CompressFunc compress;
DecompressFunc decompress;
};
void set_strategy(Strategy strategy) {
strategy_ = std::move(strategy);
}
std::string compress(const std::string& input) {
if (!strategy_.compress) return input;
return strategy_.compress(input);
}
std::string decompress(const std::string& input) {
if (!strategy_.decompress) return input;
return strategy_.decompress(input);
}
const std::string& strategy_name() const { return strategy_.name; }
private:
Strategy strategy_;
};
// 無壓縮策略
TextCompressor::Strategy no_compression() {
return {
"無壓縮",
[](const std::string& s) { return s; },
[](const std::string& s) { return s; }
};
}
// 簡化版 RLE 壓縮策略
TextCompressor::Strategy rle_compression() {
return {
"RLE 壓縮",
[](const std::string& input) -> std::string {
if (input.empty()) return "";
std::ostringstream oss;
char current = input[0];
int count = 1;
for (std::size_t i = 1; i < input.size(); ++i) {
if (input[i] == current) {
++count;
} else {
oss << count << current;
current = input[i];
count = 1;
}
}
oss << count << current;
return oss.str();
},
[](const std::string& input) -> std::string {
std::string result;
std::size_t i = 0;
while (i < input.size()) {
int count = 0;
while (i < input.size() && input[i] >= '0' && input[i] <= '9') {
count = count * 10 + (input[i] - '0');
++i;
}
if (i < input.size()) {
result.append(count, input[i]);
++i;
}
}
return result;
}
};
}
// 大寫轉換「壓縮」(演示用)
TextCompressor::Strategy uppercase_transform() {
return {
"大寫轉換",
[](const std::string& input) -> std::string {
std::string result = input;
std::transform(result.begin(), result.end(), result.begin(), ::toupper);
return result;
},
[](const std::string& input) -> std::string {
std::string result = input;
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
return result;
}
};
}
void demo_compression_strategy() {
std::cout << "========================================\n";
std::cout << " Strategy 模式:壓縮策略\n";
std::cout << "========================================\n\n";
TextCompressor compressor;
std::string test_data = "aaabbbcccdddeeefff";
std::vector<TextCompressor::Strategy> strategies = {
no_compression(),
rle_compression(),
uppercase_transform()
};
std::cout << "原始資料: \"" << test_data << "\" (長度: " << test_data.size() << ")\n\n";
for (auto& strategy : strategies) {
compressor.set_strategy(std::move(strategy));
std::string compressed = compressor.compress(test_data);
std::string decompressed = compressor.decompress(compressed);
std::cout << " 策略: " << compressor.strategy_name() << "\n";
std::cout << " 壓縮後: \"" << compressed << "\" (長度: " << compressed.size() << ")\n";
std::cout << " 解壓後: \"" << decompressed << "\"\n";
double ratio = static_cast<double>(compressed.size()) / test_data.size() * 100.0;
std::cout << " 壓縮率: " << std::fixed << std::setprecision(1) << ratio << "%\n\n";
}
}
// ============================================================
// 第五部分:計算策略(展示執行期切換)
// ============================================================
class Calculator {
public:
using Operation = std::function<double(double, double)>;
void register_operation(const std::string& name, Operation op) {
operations_[name] = std::move(op);
}
double calculate(const std::string& op_name, double a, double b) {
auto it = operations_.find(op_name);
if (it == operations_.end()) {
std::cout << " 未知操作: " << op_name << "\n";
return 0;
}
return it->second(a, b);
}
void list_operations() const {
std::cout << " 可用操作: ";
for (const auto& [name, _] : operations_) {
std::cout << name << " ";
}
std::cout << "\n";
}
private:
std::map<std::string, Operation> operations_;
};
void demo_calculator_strategy() {
std::cout << "========================================\n";
std::cout << " Strategy 模式:可擴充計算器\n";
std::cout << "========================================\n\n";
Calculator calc;
// 註冊基本操作
calc.register_operation("+", [](double a, double b) { return a + b; });
calc.register_operation("-", [](double a, double b) { return a - b; });
calc.register_operation("*", [](double a, double b) { return a * b; });
calc.register_operation("/", [](double a, double b) {
return b != 0 ? a / b : 0;
});
// 註冊進階操作
calc.register_operation("pow", [](double a, double b) { return std::pow(a, b); });
calc.register_operation("max", [](double a, double b) { return std::max(a, b); });
calc.register_operation("min", [](double a, double b) { return std::min(a, b); });
calc.register_operation("avg", [](double a, double b) { return (a + b) / 2.0; });
calc.list_operations();
std::cout << "\n";
// 使用不同策略計算
struct TestCase { std::string op; double a; double b; };
std::vector<TestCase> tests = {
{"+", 10, 3}, {"-", 10, 3}, {"*", 10, 3}, {"/", 10, 3},
{"pow", 2, 10}, {"max", 42, 17}, {"avg", 100, 200}
};
for (const auto& tc : tests) {
double result = calc.calculate(tc.op, tc.a, tc.b);
std::cout << " " << tc.a << " " << tc.op << " " << tc.b
<< " = " << result << "\n";
}
// 動態新增操作
std::cout << "\n 動態新增 'mod' 操作:\n";
calc.register_operation("mod", [](double a, double b) {
return std::fmod(a, b);
});
std::cout << " 10 mod 3 = " << calc.calculate("mod", 10, 3) << "\n";
std::cout << "\n";
}
// ============================================================
// 第六部分:傳統 vs 現代比較
// ============================================================
void demo_comparison() {
std::cout << "========================================\n";
std::cout << " 傳統 vs 現代 Strategy 比較\n";
std::cout << "========================================\n\n";
std::cout << " ┌─────────────────┬──────────────────┐\n";
std::cout << " │ 傳統(類別繼承) │ 現代(std::function)│\n";
std::cout << " ├─────────────────┼──────────────────┤\n";
std::cout << " │ 需要定義類別 │ 直接用 lambda │\n";
std::cout << " │ 可以持有狀態 │ lambda 也能捕獲 │\n";
std::cout << " │ 需要 unique_ptr │ 直接賦值 │\n";
std::cout << " │ 虛擬函式開銷 │ std::function 開銷 │\n";
std::cout << " │ 適合複雜策略 │ 適合簡單策略 │\n";
std::cout << " │ 容易測試 │ 更容易組合 │\n";
std::cout << " └─────────────────┴──────────────────┘\n\n";
std::cout << " 建議:\n";
std::cout << " - 策略簡單(一兩個函式)→ 用 std::function\n";
std::cout << " - 策略複雜(多個方法、有狀態)→ 用類別繼承\n";
std::cout << " - 需要執行期多型 → 用類別繼承\n";
std::cout << " - 需要靈活組合 → 用 std::function\n\n";
}
// ============================================================
// 主程式
// ============================================================
int main() {
std::cout << "╔══════════════════════════════════════╗\n";
std::cout << "║ 設計模式:Strategy 模式 ║\n";
std::cout << "╚══════════════════════════════════════╝\n\n";
demo_sort_strategy();
demo_modern_sort_strategy();
demo_payment_strategy();
demo_compression_strategy();
demo_calculator_strategy();
demo_comparison();
std::cout << "=== 程式結束 ===\n";
return 0;
}
Artículos relacionados
C++
c
Actualizado 2026-07-21
deviceAlpha.h
deviceAlpha.h — c source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/deviceAlpha.h).
Leer artículo →
C++
c
Actualizado 2026-07-21
finalproject.c
finalproject.c — c source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/finalproject.c).
Leer artículo →
C++
cpp
Actualizado 2026-07-21
finalproject.cpp
finalproject.cpp — cpp source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/finalproject.cpp).
Leer artículo →
C++
c
Actualizado 2026-07-21
deviceAlpha.h
deviceAlpha.h — c source code from the C++ learning materials (C++/Mavis_Homework/Lab8/deviceAlpha.h).
Leer artículo →
C++
c
Actualizado 2026-07-21
lab8.c
lab8.c — c source code from the C++ learning materials (C++/Mavis_Homework/Lab8/lab8.c).
Leer artículo →
C++
cpp
Actualizado 2026-07-21
lab8.cpp
lab8.cpp — cpp source code from the C++ learning materials (C++/Mavis_Homework/Lab8/lab8.cpp).
Leer artículo →