工厂模式(Factory Pattern)

一句话:把“创建对象”这件事封装起来,让使用者只关心“要什么”,不关心“怎么造”。 动机:解耦模块与具体实现,降低“新增一种产品/驱动”时的改动面;在嵌入式里还能集中管控内存与初始化流程。

1. 何时使用

2. 三种常见形态


3. 简单工厂(可运行、最直接)

场景:根据配置选择不同的 Logger。

#include <memory>
#include <string>
#include <iostream>

struct ILogger {
    virtual ~ILogger() = default;
    virtual void log(const std::string& msg) = 0;
};

struct UartLogger : ILogger {
    void log(const std::string& msg) override { std::cout << "[UART] " << msg << "\n"; }
};

struct CanLogger : ILogger {
    void log(const std::string& msg) override { std::cout << "[CAN ] " << msg << "\n"; }
};

enum class LoggerType { UART, CAN };

struct LoggerFactory {
    static std::unique_ptr<ILogger> create(LoggerType t) {
        switch (t) {
        case LoggerType::UART: return std::make_unique<UartLogger>();
        case LoggerType::CAN:  return std::make_unique<CanLogger>();
        }
        return nullptr;
    }
};

int main() {
    auto log = LoggerFactory::create(LoggerType::UART);
    log->log("hello factory");
}

优点:容易上手。 缺点:每加一个实现就得改 switch,违背开闭原则。


4. 工厂方法(扩展友好)

场景:将“生产逻辑”交给不同的工厂子类,新增产品只新增类,不动老代码。

#include <memory>
#include <string>
#include <iostream>

struct ISensor { virtual ~ISensor()=default; virtual int read() = 0; };

struct I2CSensor : ISensor { int read() override { return 100; } };
struct SPISensor : ISensor { int read() override { return 200; } };

struct ISensorFactory {
    virtual ~ISensorFactory() = default;
    virtual std::unique_ptr<ISensor> create() = 0;
};

struct I2CFactory : ISensorFactory {
    std::unique_ptr<ISensor> create() override { return std::make_unique<I2CSensor>(); }
};

struct SPIFactory : ISensorFactory {
    std::unique_ptr<ISensor> create() override { return std::make_unique<SPISensor>(); }
};

int main() {
    std::unique_ptr<ISensorFactory> f = std::make_unique<I2CFactory>();
    auto s = f->create();
    std::cout << s->read() << "\n";
}

优点:新增产品→新增工厂子类即可。 缺点:类数量增多;如果产品很多,层级也会多。


5. 抽象工厂(创建“产品族”)

场景:同一芯片厂商的一组外设驱动(UART/GPIO/SPI)需要配套创建。

#include <memory>
#include <iostream>

// 抽象产品
struct UART { virtual ~UART()=default; virtual void send(const char*)=0; };
struct GPIO { virtual ~GPIO()=default; virtual void write(int)=0; };

// 具体产品族 A
struct VendorA_UART : UART { void send(const char* s) override { std::cout << "A.UART " << s << "\n"; } };
struct VendorA_GPIO : GPIO { void write(int v) override { std::cout << "A.GPIO " << v << "\n"; } };

// 具体产品族 B
struct VendorB_UART : UART { void send(const char* s) override { std::cout << "B.UART " << s << "\n"; } };
struct VendorB_GPIO : GPIO { void write(int v) override { std::cout << "B.GPIO " << v << "\n"; } };

// 抽象工厂:成套创建
struct DriverFactory {
    virtual ~DriverFactory()=default;
    virtual std::unique_ptr<UART> createUART() = 0;
    virtual std::unique_ptr<GPIO> createGPIO() = 0;
};

struct VendorA_Factory : DriverFactory {
    std::unique_ptr<UART> createUART() override { return std::make_unique<VendorA_UART>(); }
    std::unique_ptr<GPIO> createGPIO() override { return std::make_unique<VendorA_GPIO>(); }
};

struct VendorB_Factory : DriverFactory {
    std::unique_ptr<UART> createUART() override { return std::make_unique<VendorB_UART>(); }
    std::unique_ptr<GPIO> createGPIO() override { return std::make_unique<VendorB_GPIO>(); }
};

int main() {
    std::unique_ptr<DriverFactory> f = std::make_unique<VendorA_Factory>();
    auto uart = f->createUART();
    auto gpio = f->createGPIO();
    uart->send("ping");
    gpio->write(1);
}

优点:保证同一“产品族”一致性;切换厂商只换工厂。 缺点:对“跨族混搭”支持不友好。


6. 注册式工厂(运行时按字符串/ID 创建)

场景:从配置文件/命令行/网络报文里读取产品名,然后创建对象。对扩展更友好:新增类只需注册。

#include <unordered_map>
#include <functional>
#include <memory>
#include <string>

template <typename Base>
class RegistryFactory {
public:
    using Creator = std::function<std::unique_ptr<Base>()>;

    static RegistryFactory& instance() {
        static RegistryFactory inst; // 避免静态初始化次序问题
        return inst;
    }

    void reg(const std::string& key, Creator c) { creators_[key] = std::move(c); }

    std::unique_ptr<Base> create(const std::string& key) const {
        if (auto it = creators_.find(key); it != creators_.end()) return (it->second)();
        return nullptr;
    }
private:
    std::unordered_map<std::string, Creator> creators_;
};

struct IAlgo { virtual ~IAlgo()=default; virtual int run(int)=0; };
struct AlgoA : IAlgo { int run(int x) override { return x+1; } };
struct AlgoB : IAlgo { int run(int x) override { return x*2; } };

struct AutoRegister {
    AutoRegister() {
        RegistryFactory<IAlgo>::instance().reg("A", []{ return std::make_unique<AlgoA>(); });
        RegistryFactory<IAlgo>::instance().reg("B", []{ return std::make_unique<AlgoB>(); });
    }
} _auto_register_once;

int main() {
    auto a = RegistryFactory<IAlgo>::instance().create("B");
    return a ? a->run(21) : -1; // 42
}

要点


7. 嵌入式落地指南(内存/性能/可维护)

  1. 禁用动态分配:用对象池+ placement-new。
#include <new>
#include <array>

template <typename T, size_t N>
class ObjectPool {
public:
    template <typename...Args>
    T* create(Args&&...args) {
        for (auto& slot : used_) if (!slot) {
            size_t idx = &slot - used_.data();
            slot = true;
            return new (&buf_[idx]) T(std::forward<Args>(args)...);
        }
        return nullptr; // 满了
    }
    void destroy(T* p) {
        if (!p) return;
        p->~T();
        size_t idx = (reinterpret_cast<char*>(p) - reinterpret_cast<char*>(buf_.data())) / sizeof(T);
        used_[idx] = false;
    }
private:
    std::array<std::aligned_storage_t<sizeof(T), alignof(T)>, N> buf_{};
    std::array<bool, N> used_{};
};
  1. 去虚函数:在极端性能/尺寸场景用 std::variant + std::visit 或 CRTP,避免 vtable 开销。
  2. 初始化顺序:将注册表/单例都做成函数内静态
  3. 错误处理:禁用异常时返回 nullptr/错误码,集中在工厂处理。
  4. 可测试性:工厂接收依赖注入(硬件句柄、配置结构体),单测时注入假设备。

8. 现代 C++ 替代与增强

template <class T, class...Args>
std::unique_ptr<T> make(Args&&...args) {
    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
#include <variant>
struct UartLogger { void log(const std::string& s) { /*...*/ } };
struct CanLogger  { void log(const std::string& s) { /*...*/ } };
using Logger = std::variant<UartLogger, CanLogger>;

void log(Logger& L, const std::string& s) {
    std::visit([&](auto& impl){ impl.log(s); }, L);
}

9. 反例与坑


10. 小结与选型建议


11. 常见面试角度(速记)


12. 延伸阅读与实践任务

写代码是搭积木,工厂就是“零件中心”。当零件越来越多,先把库房管好,世界才不会一开机就乱成一锅粥。