S SmartDocs
Chuỗi bài: C++ cpp 636 dòng · Cập nhật 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;
}

Bài viết liên quan