首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >前端开发者快速掌握 C++:设计模式实战(创建型 + 结构型)

前端开发者快速掌握 C++:设计模式实战(创建型 + 结构型)

原创
作者头像
骑猪耍太极
发布2026-05-19 16:15:20
发布2026-05-19 16:15:20
900
举报
文章被收录于专栏:AI编程之旅AI编程之旅

设计模式你在前端一定用过,但 C++ 的实现有其独特之处——需要和智能指针、虚函数、编译期特性结合。这篇从生产级 C++ 渲染框架中提炼了 5 个模式,展示"同一个模式在 C++ 中长什么样"。


一、工厂模式(注册制)— 最核心的扩展性模式

一句话

框架定义"创建接口",业务方注册"怎么创建",运行时按名字查表创建。

C++ 实现

代码语言:cpp
复制
// ========== 1. 定义接口和创建器类型 ==========
class IView {
public:
    virtual void Render() = 0;
    virtual ~IView() = default;
};

using ViewCreator = std::function<std::shared_ptr<IView>()>;

// ========== 2. 注册表(框架提供) ==========
class ViewFactory {
public:
    static void Register(const std::string& name, ViewCreator creator) {
        GetRegistry()[name] = std::move(creator);
    }

    static std::shared_ptr<IView> Create(const std::string& name) {
        auto& registry = GetRegistry();
        auto it = registry.find(name);
        if (it != registry.end()) {
            return it->second();
        }
        // fallback:返回通用实现
        auto fallback = registry.find("__default__");
        if (fallback != registry.end()) {
            return fallback->second();
        }
        return nullptr;
    }

private:
    static std::unordered_map<std::string, ViewCreator>& GetRegistry() {
        static std::unordered_map<std::string, ViewCreator> registry;
        return registry;
    }
};

// ========== 3. 业务方注册(启动时执行一次) ==========
class ImageView : public IView {
public:
    void Render() override { /* 渲染图片 */ }
};

class VideoView : public IView {
public:
    void Render() override { /* 渲染视频 */ }
};

// 启动时
ViewFactory::Register("ImageView", []() { return std::make_shared<ImageView>(); });
ViewFactory::Register("VideoView", []() { return std::make_shared<VideoView>(); });

// ========== 4. 框架内部使用 ==========
auto view = ViewFactory::Create("ImageView");
view->Render();

前端类比

代码语言:typescript
复制
// React 动态组件注册 — 同一思想
const componentRegistry = new Map<string, () => React.ComponentType>();

componentRegistry.set("ImageView", () => ImageView);
componentRegistry.set("VideoView", () => VideoView);

function createComponent(name: string) {
    const Component = componentRegistry.get(name)?.();
    return Component ? <Component /> : <FallbackView />;
}

C++ 特有的设计要点

  1. 返回 shared_ptr — 工厂创建的对象通过智能指针管理,调用方不用管释放
  2. static 局部变量实现注册表 — 避免全局变量初始化顺序问题(C++ 特有坑)
  3. Fallback 机制 — 未注册的名字不崩溃,降级到通用实现

适用场景

  • 插件化架构(按需加载组件)
  • 跨平台框架(不同平台注册不同实现)
  • 配置驱动的 UI(后端下发 View 类型名,前端动态创建)

二、单例模式 — 全局唯一的管理器

一句话

确保一个类只有一个实例,提供全局访问点。

C++ 实现

代码语言:cpp
复制
class EventCenter {
public:
    // 获取唯一实例
    static EventCenter& GetInstance() {
        static EventCenter instance;  // C++11 保证线程安全
        return instance;
    }

    void Subscribe(const std::string& event, std::function<void()> handler) {
        handlers_[event].push_back(std::move(handler));
    }

    void Emit(const std::string& event) {
        auto it = handlers_.find(event);
        if (it != handlers_.end()) {
            for (auto& handler : it->second) {
                handler();
            }
        }
    }

    // 禁止拷贝和赋值
    EventCenter(const EventCenter&) = delete;
    EventCenter& operator=(const EventCenter&) = delete;

private:
    // 构造函数私有,外部无法 new
    EventCenter() = default;
    std::unordered_map<std::string, std::vector<std::function<void()>>> handlers_;
};

// 使用
EventCenter::GetInstance().Subscribe("ready", []() { /* ... */ });
EventCenter::GetInstance().Emit("ready");

前端类比

代码语言:typescript
复制
// TS 单例 — 思路一致但 JS 更简单
class EventCenter {
    private static instance: EventCenter;
    static getInstance() {
        if (!this.instance) this.instance = new EventCenter();
        return this.instance;
    }
    private constructor() {}
}

// 或者更 JS 的方式:直接导出一个对象
export const eventCenter = { handlers: {}, subscribe() {}, emit() {} };

C++ 特有的设计要点

  1. = delete 禁止拷贝 — JS 中对象天然不会被"拷贝出一份新的",但 C++ 会(值语义),必须显式禁止
  2. static 局部变量 — C++11 保证线程安全的懒初始化,比双重检查锁更简洁
  3. 返回引用 & — 避免指针判空,调用方直接 . 操作

适用场景

  • 事件分发中心
  • 配置管理器
  • 资源适配器管理器
  • 线程池

三、代理模式 — 透明插入额外逻辑

一句话

在不改变接口的前提下,在真实对象前面加一层"中间人",做额外的事情。

C++ 实现

代码语言:cpp
复制
// ========== 接口 ==========
class IRenderLayer {
public:
    virtual void CreateView(int tag, const std::string& name) = 0;
    virtual void SetProp(int tag, const std::string& key, const std::string& value) = 0;
    virtual void RemoveView(int tag) = 0;
    virtual ~IRenderLayer() = default;
};

// ========== 真实实现 ==========
class RenderLayerImpl : public IRenderLayer {
public:
    void CreateView(int tag, const std::string& name) override {
        // 真正创建原生 UI 节点
    }
    void SetProp(int tag, const std::string& key, const std::string& value) override {
        // 真正设置属性
    }
    void RemoveView(int tag) override {
        // 真正删除节点
    }
};

// ========== 代理层(缓存加速) ==========
class CacheProxyLayer : public IRenderLayer {
public:
    CacheProxyLayer()
        : real_layer_(std::make_shared<RenderLayerImpl>()) {}

    void CreateView(int tag, const std::string& name) override {
        // 额外逻辑:同时维护一棵虚拟节点树
        virtual_tree_[tag] = {name, {}};
        // 委托真实渲染层
        if (!is_using_cache_) {
            real_layer_->CreateView(tag, name);
        }
    }

    void SetProp(int tag, const std::string& key, const std::string& value) override {
        // 额外逻辑:记录到虚拟树
        virtual_tree_[tag].props[key] = value;
        // 委托真实渲染层
        if (!is_using_cache_) {
            real_layer_->SetProp(tag, key, value);
        }
    }

    void RemoveView(int tag) override {
        virtual_tree_.erase(tag);
        if (!is_using_cache_) {
            real_layer_->RemoveView(tag);
        }
    }

private:
    std::shared_ptr<RenderLayerImpl> real_layer_;
    std::unordered_map<int, VirtualNode> virtual_tree_;
    bool is_using_cache_ = false;
};

前端类比

代码语言:typescript
复制
// JS Proxy — 语言内置支持
const realApi = { fetchData() { /* 真实请求 */ } };

const cachedApi = new Proxy(realApi, {
    get(target, prop) {
        return (...args) => {
            if (cache.has(args)) return cache.get(args);  // 额外逻辑
            return target[prop](...args);                   // 委托真实对象
        };
    }
});

C++ 特有的设计要点

  1. 通过接口(基类)实现 — 代理和真实对象实现同一个接口,调用方无感知
  2. 组合优于继承 — 代理层内部持有真实对象的 shared_ptr,不是继承它
  3. 可以控制是否委托if (!is_using_cache_) 让代理可以"拦截"而不是"转发"

适用场景

  • 缓存层(缓存 UI 首屏,加速二次启动)
  • 日志层(透明记录所有调用)
  • 权限控制(拦截无权限的操作)
  • 延迟加载(先返回占位,后台加载完再替换)

四、适配器模式 — 对接不同实现

一句话

定义统一接口,让不同的具体实现可以互换插入。

C++ 实现

代码语言:cpp
复制
// ========== 框架定义的统一接口 ==========
class IImageLoader {
public:
    virtual void Load(const std::string& url, std::function<void(Image)> callback) = 0;
    virtual ~IImageLoader() = default;
};

class ILogger {
public:
    virtual void Info(const std::string& tag, const std::string& msg) = 0;
    virtual void Error(const std::string& tag, const std::string& msg) = 0;
    virtual ~ILogger() = default;
};

// ========== 适配器管理器 ==========
class AdapterManager {
public:
    static AdapterManager& GetInstance() {
        static AdapterManager instance;
        return instance;
    }

    void SetImageLoader(std::shared_ptr<IImageLoader> loader) { image_loader_ = loader; }
    void SetLogger(std::shared_ptr<ILogger> logger) { logger_ = logger; }

    IImageLoader* GetImageLoader() { return image_loader_.get(); }
    ILogger* GetLogger() { return logger_.get(); }

private:
    std::shared_ptr<IImageLoader> image_loader_;
    std::shared_ptr<ILogger> logger_;
};

// ========== App A 的实现 ==========
class GlideImageLoader : public IImageLoader {
    void Load(const std::string& url, std::function<void(Image)> callback) override {
        // 用 Glide 加载
    }
};

// ========== App B 的实现 ==========
class CoilImageLoader : public IImageLoader {
    void Load(const std::string& url, std::function<void(Image)> callback) override {
        // 用 Coil 加载
    }
};

// ========== 宿主 App 启动时注入 ==========
AdapterManager::GetInstance().SetImageLoader(std::make_shared<GlideImageLoader>());

前端类比

代码语言:typescript
复制
// 依赖注入 — 前端框架中很常见
interface IImageLoader {
    load(url: string): Promise<HTMLImageElement>;
}

// 框架内部通过接口调用,不关心具体实现
class ImageView {
    constructor(private loader: IImageLoader) {}
    render(url: string) {
        this.loader.load(url).then(img => { /* 渲染 */ });
    }
}

// 不同环境注入不同实现
const loader = isNode ? new SharpLoader() : new BrowserLoader();
new ImageView(loader);

适用场景

  • SDK 框架(不同宿主 App 有不同的基础设施)
  • 跨平台层(同一接口,各平台不同实现)
  • 测试(注入 Mock 实现)

五、对象池模式 — 复用代替创建销毁

一句话

对象用完不销毁,放回池子里,下次需要时直接取出来复用。

C++ 实现

代码语言:cpp
复制
class ViewPool {
public:
    // 从池中取一个可复用的 View(没有就返回 nullptr)
    std::shared_ptr<IView> Acquire(const std::string& view_name) {
        auto it = pool_.find(view_name);
        if (it != pool_.end() && !it->second.empty()) {
            auto view = it->second.back();
            it->second.pop_back();
            return view;
        }
        return nullptr;
    }

    // 用完放回池中
    void Release(const std::string& view_name, std::shared_ptr<IView> view) {
        view->Reset();  // 重置为初始状态
        pool_[view_name].push_back(std::move(view));
    }

    // 清空池子(内存紧张时调用)
    void Clear() {
        pool_.clear();
    }

private:
    std::unordered_map<std::string, std::vector<std::shared_ptr<IView>>> pool_;
};

// ========== 框架使用 ==========
std::shared_ptr<IView> GetOrCreateView(const std::string& name) {
    // 先尝试从池中取
    auto view = pool.Acquire(name);
    if (view) return view;
    // 池中没有,新建
    return ViewFactory::Create(name);
}

void RecycleView(const std::string& name, std::shared_ptr<IView> view) {
    if (view->CanReuse()) {
        pool.Release(name, view);  // 可复用:回收
    } else {
        view->Destroy();           // 不可复用:销毁
    }
}

前端类比

代码语言:typescript
复制
// React 中的 key 复用 + RecyclerView 思想
// 前端通常靠虚拟 DOM diff 实现复用,不需要手动管理池子
// 但 Canvas 游戏中经常有对象池:
class BulletPool {
    private pool: Bullet[] = [];

    acquire(): Bullet {
        return this.pool.pop() ?? new Bullet();
    }

    release(bullet: Bullet) {
        bullet.reset();
        this.pool.push(bullet);
    }
}

C++ 特有的设计要点

  1. Reset() 方法 — 复用前必须清理上一次的状态,否则数据"串台"
  2. CanReuse() 判断 — 不是所有对象都适合复用(如正在播动画的 View)
  3. 内存回收时机 — 需要监听内存警告,及时 Clear() 释放池中对象
  4. 智能匹配 — 高级复用池支持"找最匹配的对象"而非随机取一个

适用场景

  • 滚动列表的 View 复用(对应 RecyclerView / UITableView)
  • 游戏中的子弹/粒子/特效对象
  • 数据库连接池
  • 线程池

总结

模式

核心诉求

C++ 关键实现手段

工厂(注册制)

解耦创建和使用

static 注册表 + shared_ptr 返回

单例

全局唯一

static 局部变量 + = delete

代理

透明插入逻辑

实现同一接口 + 内部组合真实对象

适配器

统一不同实现

纯虚接口 + 运行时注入

对象池

避免频繁创建销毁

vector 存储 + Reset 清理状态

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、工厂模式(注册制)— 最核心的扩展性模式
    • 一句话
    • C++ 实现
    • 前端类比
    • C++ 特有的设计要点
    • 适用场景
  • 二、单例模式 — 全局唯一的管理器
    • 一句话
    • C++ 实现
    • 前端类比
    • C++ 特有的设计要点
    • 适用场景
  • 三、代理模式 — 透明插入额外逻辑
    • 一句话
    • C++ 实现
    • 前端类比
    • C++ 特有的设计要点
    • 适用场景
  • 四、适配器模式 — 对接不同实现
    • 一句话
    • C++ 实现
    • 前端类比
    • 适用场景
  • 五、对象池模式 — 复用代替创建销毁
    • 一句话
    • C++ 实现
    • 前端类比
    • C++ 特有的设计要点
    • 适用场景
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档